/* * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors * Copyright (C) 2004-2014 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 "aclass.h" #include "borders.h" #include "cursors.h" #include "desktops.h" #include "ecompmgr.h" #include "emodule.h" #include "eobj.h" #include "events.h" #include "ewins.h" #include "focus.h" #include "grabs.h" #include "groups.h" #include "hints.h" #include "screen.h" #include "slide.h" #include "snaps.h" #include "windowmatch.h" #include "xwin.h" #include #include #define EWIN_TOP_EVENT_MASK \ (EnterWindowMask | LeaveWindowMask) #define EWIN_CONTAINER_EVENT_MASK \ (SubstructureNotifyMask | SubstructureRedirectMask) #define EWIN_CLIENT_EVENT_MASK \ (FocusChangeMask | ResizeRedirectMask | \ PropertyChangeMask | ColormapChangeMask | VisibilityChangeMask) static void EwinChangesStart(EWin * ewin); static void EwinChangesProcess(EWin * ewin); static void EwinHandleEventsToplevel(Win win, XEvent * ev, void *prm); #if USE_CONTAINER_WIN static void EwinHandleEventsContainer(Win win, XEvent * ev, void *prm); #endif static void EwinHandleEventsClient(Win win, XEvent * ev, void *prm); static void EwinUnmap1(EWin * ewin); static void EwinUnmap2(EWin * ewin); EX_Window EwinGetClientXwin(const EWin * ewin) { Win win = EwinGetClientWin(ewin); return (win) ? WinGetXwin(win) : NoXID; } static EWin * EwinCreate(int type) { EWin *ewin; ewin = ECALLOC(EWin, 1); ewin->type = type; ewin->state.state = (Mode.wm.startup) ? EWIN_STATE_STARTUP : EWIN_STATE_NEW; ewin->o.stacked = -1; /* Not placed on desk yet */ EoSetDesk(ewin, DesksGetCurrent()); EoSetLayer(ewin, 4); EoSetFade(ewin, 1); EoSetShadow(ewin, 1); ewin->update.shape = 1; ewin->update.border = 1; ewin->save_max.x = ewin->save_max.y = ewin->save_max.w = ewin->save_max.h = -1; ewin->save_fs.x = ewin->save_fs.y = ewin->save_fs.w = ewin->save_fs.h = -1; ewin->save_fs.layer = -1; ewin->icccm.need_input = 1; ewin->icccm.width_min = 0; ewin->icccm.height_min = 0; ewin->icccm.width_max = 65535; ewin->icccm.height_max = 65535; ewin->icccm.base_w = 0; ewin->icccm.base_h = 0; ewin->icccm.w_inc = 1; ewin->icccm.h_inc = 1; ewin->icccm.aspect_min = 0.f; ewin->icccm.aspect_max = 65535.f; ewin->icccm.grav = NorthWestGravity; ewin->area_x = -1; ewin->area_y = -1; ewin->place.gravity = -1; return ewin; } static int EwinGetAttributes(EWin * ewin, Win win, EX_Window xwin, XWindowAttributes * pxwa) { XWindowAttributes xwa; if (!win) { win = ERegisterWindow(xwin, pxwa); if (!win) return -1; } EGetWindowAttributes(win, &xwa); ewin->client.win = win; ewin->client.x = ewin->save_max.x = ewin->save_fs.x = xwa.x; ewin->client.y = ewin->save_max.y = ewin->save_fs.y = xwa.y; ewin->client.w = ewin->save_max.w = ewin->save_fs.w = xwa.width; ewin->client.h = ewin->save_max.h = ewin->save_fs.h = xwa.height; ewin->client.bw = xwa.border_width; if (EDebug(EDBUG_TYPE_SNAPS)) Eprintf("Snap get attr %#x: %4d+%4d %4dx%4d: %s\n", EwinGetClientXwin(ewin), ewin->client.x, ewin->client.y, ewin->client.w, ewin->client.h, EwinGetTitle(ewin)); return 0; } static void EwinGetHints(EWin * ewin) { if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x\n", __func__, EwinGetClientXwin(ewin)); ICCCM_GetTitle(ewin); ICCCM_GetHints(ewin); ICCCM_GetGeoms(ewin); MWM_GetHints(ewin, 0); ICCCM_GetInfo(ewin); /* NB! Need group info first */ HintsGetWindowHints(ewin); SessionGetInfo(ewin); } static void EwinHintsInferProps(EWin * ewin) { if (ewin->ewmh.type.b.desktop) { EoSetLayer(ewin, 0); if (!ewin->state.identified) EoSetSticky(ewin, 1); ewin->props.focusclick = 1; ewin->props.skip_focuslist = 1; EwinInhSetUser(ewin, move, 1); EwinInhSetUser(ewin, size, 1); ewin->props.donthide = 1; ewin->props.no_border = 1; } if (ewin->ewmh.type.b.dock) { ewin->props.skip_ext_task = 1; ewin->props.skip_winlist = 1; ewin->props.skip_focuslist = 1; if (!ewin->state.identified) EoSetSticky(ewin, 1); ewin->props.donthide = 1; } if (ewin->ewmh.type.b.utility) { /* Epplets hit this */ ewin->props.skip_ext_task = 1; ewin->props.skip_winlist = 1; ewin->props.skip_focuslist = 1; ewin->props.never_use_area = 1; ewin->props.donthide = 1; } } static void EwinManage(EWin * ewin) { XSetWindowAttributes att; Win frame; int type; /* There seems to be a shape related problem when window dimensions * are >= 32768. Also, it looks like it is not possible(?) to create * pixmaps with dimensions >= 32768. * So, limit to 32000 leaving room for borders. */ if (ewin->client.w <= 0) ewin->client.w = 100; else if (ewin->client.w > 32000) ewin->client.w = 32000; if (ewin->client.h <= 0) ewin->client.h = 100; else if (ewin->client.h > 32000) ewin->client.h = 32000; if (ewin->state.docked) ewin->inh_wm.b.border = 1; ewin->serial = NextRequest(disp); frame = EoGetWin(ewin); if (!frame) { type = (ewin->props.no_argb) ? WIN_TYPE_NO_ARGB : WIN_TYPE_CLIENT; frame = ECreateObjectWindow(VROOT, ewin->client.x, ewin->client.y, ewin->client.w, ewin->client.h, 0, type, EwinGetClientWin(ewin)); EoInit(ewin, EOBJ_TYPE_EWIN, frame, ewin->client.x, ewin->client.y, ewin->client.w, ewin->client.h, 1, NULL); EventCallbackRegister(EoGetWin(ewin), EwinHandleEventsToplevel, ewin); #if USE_CONTAINER_WIN ewin->win_container = ECreateWindow(frame, 0, 0, ewin->client.w, ewin->client.h, 0); EventCallbackRegister(ewin->win_container, EwinHandleEventsContainer, ewin); #endif EventCallbackRegister(EwinGetClientWin(ewin), EwinHandleEventsClient, ewin); EobjListFocusAdd(&ewin->o, 1); EobjListOrderAdd(&ewin->o); } #if USE_CONTAINER_WIN 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); #endif #if USE_CONTAINER_WIN att.event_mask = EWIN_TOP_EVENT_MASK; #else att.event_mask = EWIN_TOP_EVENT_MASK | EWIN_CONTAINER_EVENT_MASK; #endif att.do_not_propagate_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask; #if USE_XI2 EChangeWindowAttributes(EoGetWin(ewin), CWDontPropagate, &att); ESelectInput(EoGetWin(ewin), att.event_mask); #else EChangeWindowAttributes(EoGetWin(ewin), CWEventMask | CWDontPropagate, &att); #endif ewin->client.event_mask = EWIN_CLIENT_EVENT_MASK; ESelectInput(EwinGetClientWin(ewin), ewin->client.event_mask); if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x frame=%#x cont=%#x st=%d\n", __func__, EwinGetClientXwin(ewin), EoGetXwin(ewin), EwinGetContainerXwin(ewin), ewin->state.state); if (!EwinIsInternal(ewin)) { XShapeSelectInput(disp, EwinGetClientXwin(ewin), ShapeNotifyMask); ESetWindowBorderWidth(EwinGetClientWin(ewin), 0); ewin->client.bw = 0; } ICCCM_AdoptStart(ewin); #if USE_CONTAINER_WIN /* We must reparent after getting original window position */ EReparentWindow(EwinGetClientWin(ewin), ewin->win_container, 0, 0); #else EReparentWindow(EwinGetClientWin(ewin), frame, 0, 0); #endif /* Something (e.g. a match) may have changed the window size */ EResizeWindow(EwinGetClientWin(ewin), ewin->client.w, ewin->client.h); EwinUpdateShapeInfo(ewin); ModulesSignal(ESIGNAL_EWIN_CREATE, ewin); } /* * Derive frame window geometry from client window properties */ static void EwinSetGeometry(EWin * ewin) { int x, y, l, r, t, b; int grav; grav = (ewin->state.identified) ? StaticGravity : ewin->icccm.grav; EwinGetPosition(ewin, ewin->client.x, ewin->client.y, 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); } static void EwinConfigure(EWin * ewin) { EwinStateUpdate(ewin); if (!EwinIsInternal(ewin) && Mode.wm.startup) EHintsGetInfo(ewin); /* E restart hints */ EwinHintsInferProps(ewin); SnapshotEwinApply(ewin); /* Apply saved settings */ if (ewin->save_fs.layer < 0) ewin->save_fs.layer = EoGetLayer(ewin); EwinStateUpdate(ewin); /* Update after snaps etc. */ ICCCM_Adopt(ewin); EwinBorderSelect(ewin); /* Select border before calculating geometry */ EwinSetGeometry(ewin); /* Calculate window geometry before border parts */ EwinBorderSetTo(ewin, NULL); if (!ewin->props.no_button_grabs) GrabButtonGrabs(EoGetWin(ewin)); if (ewin->state.shaded) EwinInstantShade(ewin, 1); EwinUpdateOpacity(ewin); if ((ewin->border) && (!strcmp(ewin->border->name, "BORDERLESS")) && EoGetWin(ewin)->argb) EoSetShadow(ewin, 0); HintsSetWindowState(ewin); HintsSetWindowOpacity(ewin); HintsSetClientList(); if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, EwinGetTitle(ewin)); } static void EwinCleanup(EWin * ewin) { EwinBorderDetach(ewin); } static void EwinDestroy(EWin * ewin) { EWin **lst; int i, num; if (!ewin) return; EwinUnmap1(ewin); if (EoIsMapped(ewin)) { EoUnmap(ewin); EwinUnmap2(ewin); } if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, EwinGetTitle(ewin)); EventCallbackUnregister(EoGetWin(ewin), EwinHandleEventsToplevel, ewin); #if USE_CONTAINER_WIN EventCallbackUnregister(ewin->win_container, EwinHandleEventsContainer, ewin); #endif EventCallbackUnregister(EwinGetClientWin(ewin), 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; } Efree(lst); EwinCleanup(ewin); EobjListOrderDel(&ewin->o); EobjListFocusDel(&ewin->o); EoFini(ewin); HintsSetClientList(); Efree(ewin->icccm.wm_icon_name); Efree(ewin->icccm.wm_role); Efree(ewin->icccm.wm_command); Efree(ewin->icccm.wm_machine); Efree(ewin->ewmh.wm_name); Efree(ewin->ewmh.wm_icon_name); Efree(ewin->ewmh.wm_icon); Efree(ewin->bits); Efree(ewin->session_id); PmapMaskFree(&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 <= WinGetW(VROOT)) && (DesktopAt(xd + x + dx + w - 1, yd) != dsk)))) dofloat = 1; break; case 1: if (((x + dx + w > WinGetW(VROOT)) || ((x + dx >= 0) && (DesktopAt(xd + x + dx, yd) != dsk)))) dofloat = 1; break; case 2: if (((y + dy < 0) || ((y + dy + h <= WinGetH(VROOT)) && (DesktopAt(xd, yd + y + dy + h - 1) != dsk)))) dofloat = 1; break; case 3: if (((y + dy + h > WinGetH(VROOT)) || ((y + dy >= 0) && (DesktopAt(xd, yd + y + dy) != dsk)))) dofloat = 1; break; } if (dofloat) EwinOpFloatAt(ewin, OPSRC_USER, x + xd, y + yd); } } EWin * GetEwinPointerInClient(void) { int px, py; EWin *const *lst, *ewin; int i, num; Desk *dsk; dsk = DesktopAt(Mode.events.cx, Mode.events.cy); 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)) && EoIsMapped(ewin)) return ewin; } return NULL; } EWin * GetFocusEwin(void) { return Mode.focuswin; } EWin * GetContextEwin(void) { EWin *ewin; ewin = Mode.context_ewin; if (ewin) goto done; ewin = NULL; done: #if 0 Eprintf("%s %#x %s\n", __func__, EwinGetClientXwin(ewin), EwinGetTitle(ewin)); #endif return ewin; } void SetContextEwin(EWin * ewin) { if (ewin && ewin->type == EWIN_TYPE_MENU) return; #if 0 Eprintf("%s %#x %s\n", __func__, EwinGetClientXwin(ewin), EwinGetTitle(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 grav, int *px, int *py) { int bw, bd_lr, bd_tb; bw = ewin->client.bw; bd_lr = ewin->border->border.left + ewin->border->border.right; bd_tb = ewin->border->border.top + ewin->border->border.bottom; if (grav == 0) grav = ewin->icccm.grav; switch (grav) { case NorthWestGravity: case WestGravity: case SouthWestGravity: x -= bw; break; case NorthGravity: case CenterGravity: case SouthGravity: x -= bd_lr / 2; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: 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 / 2; 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; } /* * Keep resizing on-screen window on-screen */ static void EwinKeepOnScreen(const EWin * ewin, int wn, int hn, int *px, int *py) { int x = *px, y = *py, w, h; int sx, sy, sw, sh, xy; w = EoGetW(ewin); h = EoGetH(ewin); ScreenGetAvailableArea(x, y, &sx, &sy, &sw, &sh, Conf.place.ignore_struts); /* Quit if not on-screen to begin with */ if (x < sx || x + w > sx + sw || y < sy || y + h > sy + sh) return; /* Attempt to keep on-screen */ xy = sx + sw - (w - ewin->client.w + wn); if (x > xy) x = xy; if (x < sx) x = sx; xy = sy + sh - (h - ewin->client.h + hn); if (y > xy) y = xy; if (y < sy) y = sy; *px = x; *py = y; } void EwinUpdateShapeInfo(EWin * ewin) { ewin->state.shaped = #if USE_CONTAINER_WIN EShapeSetShape(ewin->win_container, 0, 0, EwinGetClientWin(ewin)); #else EShapeUpdate(EwinGetClientWin(ewin)); #endif if (EDebug(EX_EVENT_SHAPE_NOTIFY)) Eprintf("%s %#x cont=%#x shaped=%d\n", __func__, EwinGetClientXwin(ewin), EwinGetContainerXwin(ewin), ewin->state.shaped); } void EwinPropagateShapes(EWin * ewin) { if (!EoIsShown(ewin)) return; if (ewin->state.docked) return; if (!ewin->update.shape) return; if (EDebug(EX_EVENT_SHAPE_NOTIFY)) Eprintf("%s %#x frame=%#x shaped=%d\n", __func__, EwinGetClientXwin(ewin), EoGetXwin(ewin), ewin->state.shaped); EoShapeUpdate(ewin, 1); ewin->update.shape = 0; } void EwinStateUpdate(EWin * ewin) { int fs_zo; fs_zo = ewin->state.fullscreen || ewin->state.zoomed; ewin->state.inhibit_actions = ewin->props.no_actions; ewin->state.inhibit_focus = !(ewin->icccm.need_input || ewin->icccm.take_focus) || EwinInhGetWM(ewin, focus) || ewin->state.iconified; ewin->state.inhibit_move = EwinInhGetUser(ewin, move) || fs_zo; ewin->state.inhibit_resize = ewin->state.iconified || ewin->state.shaded || (ewin->props.no_resize_h && ewin->props.no_resize_v) || EwinInhGetUser(ewin, size) || fs_zo; ewin->state.inhibit_iconify = EwinInhGetWM(ewin, iconify); ewin->state.inhibit_shade = ewin->state.no_border || ewin->state.iconified || fs_zo; ewin->state.inhibit_stick = 0; ewin->state.inhibit_max_hor = ewin->state.inhibit_resize || ewin->props.no_resize_h || fs_zo; ewin->state.inhibit_max_ver = ewin->state.inhibit_resize || ewin->props.no_resize_v || fs_zo; ewin->state.inhibit_fullscreeen = ewin->state.inhibit_move || ewin->state.inhibit_resize; ewin->state.inhibit_change_desk = ewin->state.iconified; ewin->state.inhibit_close = EwinInhGetApp(ewin, close) || EwinInhGetUser(ewin, close); ewin->state.donthide = ewin->props.donthide || ewin->props.skip_ext_task || ewin->props.skip_winlist || ewin->props.skip_focuslist; SnapshotEwinUpdate(ewin, SNAP_USE_FLAGS); } static void AddToFamily(EWin * ewin, EX_Window xwin, XWindowAttributes * pxwa, int startup) { XWindowAttributes attr; EWin *ewin2; EWin **lst; int i, k, num, fx, fy, x, y; char doslide, manplace; Desk *dsk; EGrabServer(); if (!pxwa) { pxwa = &attr; if (!EXGetWindowAttributes(xwin, &attr)) goto done; } #ifndef __cplusplus #define c_class class #endif if (pxwa->c_class != InputOutput) goto done; if (ewin) EwinCleanup(ewin); else ewin = EwinCreate(EWIN_TYPE_NORMAL); if (!ewin) goto done; if (EwinGetAttributes(ewin, NULL, xwin, pxwa)) { if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("Window is gone %#x\n", xwin); /* We got here by MapRequest. DestroyNotify should follow. */ goto done; } EwinGetHints(ewin); WindowMatchEwinOps(ewin); /* Window matches */ EwinManage(ewin); EwinConfigure(ewin); if (startup) ewin->state.placed = 1; /* 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 == NoXID || ewin->icccm.transient_for == WinGetXwin(VROOT)) { /* Group transient */ ewin->icccm.transient_for = WinGetXwin(VROOT); #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); doslide = manplace = 0; if (Mode.place.enable_features > 0 && !ewin->state.snapstarted) { /* if set for borderless then dont slide it in */ if (Conf.place.slidein && !ewin->state.no_border && dsk == DesksGetCurrent()) doslide = 1; if (Conf.place.manual && !Mode.place.doing_manual && !ewin->state.placed && !ewin->icccm.transient) manplace = 1; } 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) { EwinOpFullscreen(ewin, OPSRC_WM, 2); ewin->state.placed = 1; doslide = manplace = 0; x = EoGetX(ewin); y = EoGetY(ewin); } else if (!ewin->state.identified && (ewin->state.maximized_horz || ewin->state.maximized_vert)) { int hor, ver; /* New client requested maximisation */ hor = ewin->state.maximized_horz; ver = ewin->state.maximized_vert; ewin->state.maximized_horz = ewin->state.maximized_vert = 0; MaxSizeHV(ewin, "absolute", hor, ver); /* Set old state to current maximized one */ ewin->save_max.x = EoGetX(ewin); ewin->save_max.y = EoGetY(ewin); ewin->save_max.w = ewin->client.w; ewin->save_max.h = ewin->client.h; ewin->state.placed = 0; } else { EwinResize(ewin, ewin->client.w, ewin->client.h, 0); } /* 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 (manplace && GrabPointerSet(VROOT, ECSR_GRAB, 0) != GrabSuccess) manplace = 0; /* if it hasn't been placed yet.... find a spot for it */ if ((!ewin->state.placed) && (!manplace)) { int cx, cy, sx, sy, sw, sh; /* Place the window below the mouse pointer */ if (Conf.place.manual_mouse_pointer) { /* 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); EventsUpdateXY(&cx, &cy); ScreenGetAvailableArea(cx, cy, &sx, &sy, &sw, &sh, Conf.place.ignore_struts); /* try to center the window on the mouse pointer */ x = cx - EoGetW(ewin) / 2; y = cy - EoGetH(ewin) / 2; /* keep it all on this screen if possible */ x = MIN(x, sx + sw - EoGetW(ewin)); y = MIN(y, sy + sh - EoGetH(ewin)); x = MAX(x, sx); y = MAX(y, sy); } else if (ewin->ewmh.type.b.dialog) { /* Center unplaced dialogs on parent(if transient) or root */ Win parent; ewin2 = NULL; if (EwinGetTransientFor(ewin) != NoXID) ewin2 = EwinFindByClient(EwinGetTransientFor(ewin)); parent = (ewin2) ? EoGetWin(ewin2) : VROOT; cx = WinGetX(parent); cy = WinGetY(parent); x = cx + (WinGetW(parent) - EoGetW(ewin)) / 2; y = cy + (WinGetH(parent) - EoGetH(ewin)) / 2; ScreenGetAvailableArea(cx, cy, &sx, &sy, &sw, &sh, Conf.place.ignore_struts); /* keep it all on this screen if possible */ x = MIN(x, sx + sw - EoGetW(ewin)); y = MIN(y, sy + sh - EoGetH(ewin)); x = MAX(x, sx); y = MAX(y, sy); } else { ArrangeEwinXY(ewin, &x, &y); } ewin->state.placed = 1; } /* if we should slide it in and are not currently in the middle of a slide */ if ((manplace) && (!ewin->state.placed)) { int cx, cy; /* 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); EventsUpdateXY(&cx, &cy); ewin->state.placed = 1; x = cx - 8; y = cy - 8; GrabPointerSet(VROOT, ECSR_GRAB, 0); EoSetFloating(ewin, 1); /* Causes reparenting to root */ EwinOpFloatAt(ewin, OPSRC_USER, x, y); EwinShow(ewin); Mode.place.doing_manual = 1; MoveResizeMoveStart(ewin, 0, 0, 0); goto done; } else if (doslide) { k = rand() % 4; if (k == 0) { fx = (rand() % (WinGetW(VROOT))) - EoGetW(ewin); fy = -EoGetH(ewin); } else if (k == 1) { fx = (rand() % (WinGetW(VROOT))); fy = WinGetH(VROOT); } else if (k == 2) { fx = -EoGetW(ewin); fy = (rand() % (WinGetH(VROOT))); } else { fx = WinGetW(VROOT); fy = (rand() % (WinGetH(VROOT))) - EoGetH(ewin); } EwinMoveToDesktopAt(ewin, dsk, fx, fy); EwinShow(ewin); ewin->req_x = x; ewin->req_y = y; EwinSlideTo(ewin, EoGetX(ewin), EoGetY(ewin), ewin->req_x, ewin->req_y, Conf.place.slidespeedmap, Conf.place.slidemode, SLIDE_FOCUS | SLIDE_WARP | SLIDE_SOUND); } else { EwinMoveToDesktopAt(ewin, dsk, x, y); EwinShow(ewin); } done: EUngrabServer(); } EWin * AddInternalToFamily(Win win, const char *bname, int type, const EWinOps * ops, void *ptr) { EWin *ewin; EGrabServer(); ewin = EwinCreate(type); if (!ewin) goto done; ewin->props.donthide = 1; EwinGetAttributes(ewin, win, NoXID, NULL); WindowMatchEwinOps(ewin); /* Window matches */ EwinManage(ewin); ewin->data = ptr; ewin->ops = ops; if (ops && ops->Init) ops->Init(ewin); /* Type specific initialisation */ if (bname) ewin->border = BorderFind(bname); EwinConfigure(ewin); #if 0 Eprintf("Desk=%d, layer=%d, sticky=%d, floating=%d\n", EoGetDeskNum(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 */ Zoom(ewin, 0); MoveResizeEnd(ewin); DrawEwinShapeEnd(ewin); } static void EwinUnmap2(EWin * ewin) { /* The frame has been unmapped */ FocusToEWin(ewin, FOCUS_EWIN_UNMAP); 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, Win to) { int x, y; /* Only external clients should go here */ if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, EwinGetTitle(ewin)); EGrabServer(); ESelectInput(EwinGetClientWin(ewin), NoEventMask); XShapeSelectInput(disp, EwinGetClientXwin(ewin), NoEventMask); if (EXWindowGetParent(EwinGetClientXwin(ewin)) == EwinGetContainerXwin(ewin)) { /* Park the client window on the new root */ x = ewin->client.x; y = ewin->client.y; ETranslateCoordinates(EwinGetClientWin(ewin), VROOT, -ewin->border->border.left, -ewin->border->border.top, &x, &y, NULL); EReparentWindow(EwinGetClientWin(ewin), to, x, y); HintsDelWindowHints(ewin); } ICCCM_Withdraw(ewin); ESync(0); EUngrabServer(); } static void EwinEventMapRequest(EWin * ewin, XEvent * ev) { EX_Window xwin; xwin = ev->xmaprequest.window; if (ewin) { if (ewin->state.state == EWIN_STATE_ICONIC) EwinDeIconify(ewin); else if (ewin->state.state == EWIN_STATE_WITHDRAWN) AddToFamily(ewin, xwin, NULL, 0); else { if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s: Already managing %s %#x\n", __func__, "A", EwinGetClientXwin(ewin)); EReparentWindow(EwinGetClientWin(ewin), EwinGetContainerWin(ewin), 0, 0); } } else { /* Check if we are already managing it */ ewin = EwinFindByClient(xwin); /* Some clients MapRequest more than once ?!? */ if (ewin) { if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s: Already managing %s %#x\n", __func__, "B", EwinGetClientXwin(ewin)); EReparentWindow(EwinGetClientWin(ewin), EwinGetContainerWin(ewin), 0, 0); EwinShow(ewin); } else AddToFamily(NULL, xwin, NULL, 0); } } static void EwinEventDestroy(EWin * ewin) { if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, EwinGetTitle(ewin)); EwinDestroy(ewin); } static void EwinEventReparent(EWin * ewin, XEvent * ev) { EX_Window parent; EGrabServer(); parent = EoIsGone(ewin) ? NoXID : ev->xreparent.parent; if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d parent=%#x: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, parent, EwinGetTitle(ewin)); if (parent != EwinGetContainerXwin(ewin)) EwinDestroy(ewin); EUngrabServer(); } static void EwinEventMap(EWin * ewin, XEvent * ev) { int old_state; /* Catch clients setting OR without proper withdrawal (just unmap/map) */ if (ev->xmap.override_redirect) return; old_state = ewin->state.state; ewin->state.state = EWIN_STATE_MAPPED; if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, EwinGetTitle(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, XEvent * ev) { if (EDebug(EDBUG_TYPE_EWINS)) Eprintf("%s %#x st=%d: %s\n", __func__, EwinGetClientXwin(ewin), ewin->state.state, EwinGetTitle(ewin)); if (ewin->state.state == EWIN_STATE_STARTUP || ewin->state.state == EWIN_STATE_NEW) { #if 0 /* We get here after reparenting to container and occasionally in * other(?) situations */ Eprintf("%s %#x: Ignoring bogus Unmap event\n", __func__, EwinGetClientXwin(ewin)); #endif return; } /* Ignore synthetic events */ if (ev->xany.send_event) return; if (ewin->state.state == EWIN_STATE_WITHDRAWN) return; if (ewin->state.iconified) ewin->state.state = EWIN_STATE_ICONIC; else ewin->state.state = EWIN_STATE_WITHDRAWN; 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 (EoIsGone(ewin)) return; EwinWithdraw(ewin, VROOT); } static void EwinEventConfigureRequest(EWin * ewin, XEvent * ev) { EX_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; /* This is shady - some clients send root coords, some use the * ICCCM ones sent by us */ if (!EwinInhGetApp(ewin, move)) { #if 0 /* FIXME - ??? */ if (ev->xconfigurerequest.value_mask & CWX) x = ev->xconfigurerequest.x; if (ev->xconfigurerequest.value_mask & CWY) y = ev->xconfigurerequest.y; #else if (ev->xconfigurerequest.value_mask & CWX) x = ev->xconfigurerequest.x - (Mode.wm.win_x + EoGetX(EoGetDesk(ewin))); if (ev->xconfigurerequest.value_mask & CWY) y = ev->xconfigurerequest.y - (Mode.wm.win_y + EoGetY(EoGetDesk(ewin))); #endif } if (!EwinInhGetApp(ewin, size)) { 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 = EoGetXwin(ewin2); xwc.sibling = winrel; xwc.stack_mode = ev->xconfigurerequest.detail; if (Mode.mode == MODE_NONE) { if (xwc.stack_mode == Above) EwinRaise(ewin); else if (xwc.stack_mode == Below) EwinLower(ewin); } } if (ev->xconfigurerequest.value_mask & (CWX | CWY)) { /* Correct position taking gravity into account */ EwinGetPosition(ewin, x, y, 0, &x, &y); } else if (ev->xconfigurerequest.value_mask & (CWWidth | CWHeight)) { /* Resizing only */ EwinKeepOnScreen(ewin, w, h, &x, &y); } EwinMoveResize(ewin, x, y, w, h, MRF_NOCHECK_ONSCREEN); 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; XConfigureWindow(disp, 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, 0); ReZoom(ewin); } else { XResizeWindow(disp, ev->xresizerequest.window, ev->xresizerequest.width, ev->xresizerequest.height); } } static void EwinEventCirculateRequest(EWin * ewin, XEvent * ev) { if (ewin) { if (ev->xcirculaterequest.place == PlaceOnTop) EwinRaise(ewin); else EwinLower(ewin); } else { if (ev->xcirculaterequest.place == PlaceOnTop) XRaiseWindow(disp, ev->xcirculaterequest.window); else XLowerWindow(disp, ev->xcirculaterequest.window); } } static void EwinEventPropertyNotify(EWin * ewin, XEvent * ev) { if (EwinIsInternal(ewin)) return; EGrabServer(); EwinChangesStart(ewin); HintsProcessPropertyChange(ewin, ev); EwinStateUpdate(ewin); EwinChangesProcess(ewin); EUngrabServer(); } static void EwinEventShapeChange(EWin * ewin, XEvent * ev) { XShapeEvent *se = (XShapeEvent *) ev; if (EDebug(EX_EVENT_SHAPE_NOTIFY)) Eprintf("%s %#x %s: state.shaped=%d ev->shaped=%d\n", __func__, EwinGetClientXwin(ewin), EoGetName(ewin), ewin->state.shaped, se->shaped); if (!se->shaped && !ewin->state.shaped) return; EwinUpdateShapeInfo(ewin); ewin->update.shape = 1; EwinPropagateShapes(ewin); } static void EwinEventVisibility(EWin * ewin, int state) { ewin->state.visibility = state; } void EwinReparent(EWin * ewin, Win parent) { EwinWithdraw(ewin, parent); } int EwinRaise(EWin * ewin) { static int call_depth = 0; EWin **lst; int i, num, numt; if (call_depth > 256) return 0; call_depth++; num = EoRaise(ewin); if (EDebug(EDBUG_TYPE_RAISELOWER)) Eprintf("%s(%d) %#x %s n=%d\n", __func__, call_depth, EwinGetClientXwin(ewin), EwinGetTitle(ewin), num); if (num == 0) /* Quit if stacking is unchanged */ goto done; lst = EwinListTransients(ewin, &numt, 1); for (i = 0; i < numt; i++) EwinRaise(lst[i]); Efree(lst); if (call_depth == 1) { ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin); ClickGrabsUpdate(); } done: call_depth--; return num; } int EwinLower(EWin * ewin) { static int call_depth = 0; EWin **lst; int i, num, numt; if (call_depth > 256) return 0; call_depth++; num = EoLower(ewin); if (EDebug(EDBUG_TYPE_RAISELOWER)) Eprintf("%s(%d) %#x %s n=%d\n", __func__, call_depth, EwinGetClientXwin(ewin), EwinGetTitle(ewin), num); if (num == 0) /* Quit if stacking is unchanged */ goto done; lst = EwinListTransientFor(ewin, &numt); for (i = 0; i < numt; i++) EwinLower(lst[i]); Efree(lst); if (call_depth == 1) { ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin); ClickGrabsUpdate(); } done: call_depth--; return num; } void EwinShow(EWin * ewin) { if (EoIsShown(ewin)) return; if (EwinGetClientWin(ewin)) { EMapWindow(EwinGetClientWin(ewin)); } if (ewin->update.shape) { ewin->o.shown = 1; EwinPropagateShapes(ewin); ewin->o.shown = 0; } EoMap(ewin, 0); EwinStateUpdate(ewin); if (ewin->place.gravity < 0) EwinSetPlacementGravity(ewin, EoGetX(ewin), EoGetY(ewin)); } void EwinHide(EWin * ewin) { if (!EwinIsInternal(ewin) && (!EoIsShown(ewin) || !EoIsMapped(ewin))) return; EwinUnmap1(ewin); EUnmapWindow(EwinGetClientWin(ewin)); EoUnmap(ewin); EwinUnmap2(ewin); EwinStateUpdate(ewin); if (!EwinIsInternal(ewin) || ewin->state.iconified) return; if (EwinGetClientWin(ewin)) { ESelectInput(EwinGetClientWin(ewin), NoEventMask); XShapeSelectInput(disp, EwinGetClientXwin(ewin), NoEventMask); } if (ewin->ops && ewin->ops->Close) ewin->ops->Close(ewin); EwinDestroy(ewin); } void EwinKill(EWin * ewin) { if (EwinIsInternal(ewin)) return; XKillClient(disp, EwinGetClientXwin(ewin)); #if 0 /* Wait for unmap/destroy for now */ EwinUnmap1(ewin); EoUnmap(ewin); EwinUnmap2(ewin); EwinDestroy(ewin); #endif } void EwinSetTitle(EWin * ewin, const char *title) { HintsSetWindowName(EwinGetClientWin(ewin), title); _EFDUP(ewin->o.icccm.wm_name, title); _EFDUP(ewin->ewmh.wm_name, title); } void EwinSetClass(EWin * ewin, const char *name, const char *clss) { HintsSetWindowClass(EwinGetClientWin(ewin), name, clss); _EFDUP(ewin->o.icccm.wm_res_name, name); _EFDUP(ewin->o.icccm.wm_res_class, clss); } const char * EwinGetTitle(const EWin * ewin) { const char *name; if (!ewin) return NULL; name = ewin->ewmh.wm_name; if (name) goto done; name = EwinGetIcccmName(ewin); if (name) goto done; done: return (name && name[0]) ? name : NULL; } #if 0 /* Unused */ const char * EwinGetIconName(const EWin * ewin) { const char *name; name = ewin->ewmh.wm_icon_name; if (name) goto done; name = ewin->icccm.wm_icon_name; if (name) goto done; return EwinGetTitle(ewin); done: return (name && strlen(name)) ? name : NULL; } #endif const char * EwinBorderGetName(const EWin * ewin) { return (ewin->border) ? BorderGetName(ewin->border) : "?"; } void EwinBorderGetSize(const EWin * ewin, int *bl, int *br, int *bt, int *bb) { const Border *b = ewin->border; if (!b) { *bl = *br = *bt = *bb = 0; return; } *bl = b->border.left; *br = b->border.right; *bt = b->border.top; *bb = b->border.bottom; } void EwinBorderUpdateState(EWin * ewin) { EwinBorderDraw(ewin, 0, 0); } 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 >= WinGetW(VROOT) || y + h <= 0 || y >= WinGetH(VROOT)) return 0; return 1; } int EwinIsOnDesktop(const EWin * ewin) { int xd, yd, wd, hd; wd = WinGetW(VROOT); hd = WinGetH(VROOT); if (EoIsSticky(ewin)) { xd = yd = 0; } else { int ax, ay; DeskGetArea(EoGetDesk(ewin), &ax, &ay); xd = -ax * wd; yd = -ay * hd; wd *= Conf.desks.areas_nx; hd *= Conf.desks.areas_ny; } return EoGetX(ewin) + EoGetW(ewin) - 8 >= xd && EoGetX(ewin) + 8 <= xd + wd && EoGetY(ewin) + EoGetH(ewin) - 8 >= yd && EoGetY(ewin) + 8 <= yd + hd; } /* * 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 * WinGetW(VROOT); ewin->req_y += ay * WinGetH(VROOT); } } /* * Get saved position in relative viewport coordinates */ void EwinRememberPositionGet(EWin * ewin, Desk * dsk, int *px, int *py) { int x, y, ax, ay; x = ewin->req_x; y = ewin->req_y; if (!EoIsSticky(ewin)) { DeskGetArea(dsk, &ax, &ay); x -= ax * WinGetW(VROOT); y -= ay * WinGetH(VROOT); } *px = x; *py = y; } /* * Set placement gravity */ void EwinSetPlacementGravity(EWin * ewin, int x, int y) { int w, h, ax, ay, wd, hd; Desk *dsk; dsk = EoGetDesk(ewin); wd = EoGetW(dsk); hd = EoGetH(dsk); DeskGetArea(dsk, &ax, &ay); w = EoGetW(ewin); h = EoGetH(ewin); /* Get relative area */ ewin->place.ax = ewin->area_x; ewin->place.ay = ewin->area_y; ax = ewin->place.ax - ax; ay = ewin->place.ay - ay; x -= ax * wd; y -= ay * hd; if (x <= (wd - w) / 2) { if (y <= (hd - h) / 2) { ewin->place.gravity = EWIN_GRAVITY_NW; ewin->place.gx = x; ewin->place.gy = y; } else { ewin->place.gravity = EWIN_GRAVITY_SW; ewin->place.gx = x; ewin->place.gy = hd - (y + h); } } else { if (y <= (hd - h) / 2) { ewin->place.gravity = EWIN_GRAVITY_NE; ewin->place.gx = wd - (x + w); ewin->place.gy = y; } else { ewin->place.gravity = EWIN_GRAVITY_SE; ewin->place.gx = wd - (x + w); ewin->place.gy = hd - (y + h); } } #if 0 /* Debug */ Eprintf("Set gravity %d,%d %d,%d %d %d,%d %d,%d: %s\n", ax, ay, x, y, ewin->place.gravity, ewin->place.ax, ewin->place.ay, ewin->place.gx, ewin->place.gy, EwinGetTitle(ewin)); #endif } void EwinReposition(EWin * ewin) { int wdo, hdo, wdn, hdn; int x, y, w, h, ax, ay, xn, yn; wdo = Mode.screen.w_old; hdo = Mode.screen.h_old; wdn = WinGetW(VROOT); hdn = WinGetH(VROOT); x = EoGetX(ewin); y = EoGetY(ewin); w = EoGetW(ewin); h = EoGetH(ewin); /* Get relative area */ if (EoIsSticky(ewin)) { ax = ay = 0; } else { DeskGetArea(EoGetDesk(ewin), &ax, &ay); ax = ewin->place.ax - ax; ay = ewin->place.ay - ay; } x -= ax * wdo; y -= ay * hdo; /* Reposition to same distance from screen edges determined by * placement gravity. * Fall back to left/top if this causes left/top to go offscreen */ switch (ewin->place.gravity) { default: case EWIN_GRAVITY_NW: case EWIN_GRAVITY_SW: xn = ewin->place.gx; break; case EWIN_GRAVITY_NE: case EWIN_GRAVITY_SE: xn = wdn - w - ewin->place.gx; break; } if (x > 0 && xn < 0) xn = x; switch (ewin->place.gravity) { default: case EWIN_GRAVITY_NW: case EWIN_GRAVITY_NE: yn = ewin->place.gy; break; case EWIN_GRAVITY_SW: case EWIN_GRAVITY_SE: yn = hdn - h - ewin->place.gy; break; } if (y > 0 && yn < 0) yn = y; #if 0 /* Debug */ Eprintf("Reposition %d,%d -> %d,%d: %s\n", x, y, xn, yn, EwinGetTitle(ewin)); #endif xn += ax * wdn; yn += ay * hdn; EwinMove(ewin, xn, yn, 0); } void EwinWarpTo(EWin * ewin, int force) { if (!force && ewin == Mode.mouse_over_ewin) return; if (ewin->state.iconified) return; EWarpPointer(EoGetWin(ewin), EoGetW(ewin) / 2, EoGetH(ewin) / 2); Mode.mouse_over_ewin = ewin; } typedef union { unsigned int all; struct { unsigned char rsvd; unsigned char inh_app; unsigned char inh_user; unsigned char inh_wm; } f; } EWinMiscFlags; typedef union { unsigned int all; struct { unsigned nua:1; unsigned ctf:1; unsigned nbg:1; unsigned autoshade:1; unsigned ia:1; unsigned: 3; unsigned: 8; unsigned: 8; unsigned no_fade:1; unsigned no_shadow:1; unsigned: 6; } f; } EWinMiscFlags2; void EwinFlagsEncode(const EWin * ewin, unsigned int *flags) { EWinMiscFlags fm; EWinMiscFlags2 fm2; fm.all = 0; fm.f.inh_app = ewin->inh_app.all; fm.f.inh_user = ewin->inh_user.all; fm.f.inh_wm = ewin->inh_wm.all; fm2.all = 0; fm2.f.ia = ewin->props.ignorearrange; fm2.f.nua = ewin->props.never_use_area; fm2.f.ctf = ewin->props.focusclick; fm2.f.nbg = ewin->props.no_button_grabs; fm2.f.autoshade = ewin->props.autoshade; #if USE_COMPOSITE fm2.f.no_fade = !EoGetFade(ewin); fm2.f.no_shadow = !EoGetShadow(ewin); #endif flags[0] = fm.all; flags[1] = fm2.all; } void EwinFlagsDecode(EWin * ewin, const unsigned int *flags) { EWinMiscFlags fm; EWinMiscFlags2 fm2; fm.all = flags[0]; ewin->inh_app.all = fm.f.inh_app; ewin->inh_user.all = fm.f.inh_user; ewin->inh_wm.all = fm.f.inh_wm; fm2.all = flags[1]; ewin->props.ignorearrange = fm2.f.ia; ewin->props.never_use_area = fm2.f.nua; ewin->props.focusclick = fm2.f.ctf; ewin->props.no_button_grabs = fm2.f.nbg; ewin->props.autoshade = fm2.f.autoshade; #if USE_COMPOSITE EoSetFade(ewin, !fm2.f.no_fade); EoSetShadow(ewin, !fm2.f.no_shadow); #endif } void EwinUpdateOpacity(EWin * ewin) { unsigned int opacity; opacity = 0; if (ewin->state.moving || ewin->state.resizing) opacity = OpacityFromPercent(Conf.opacity.movres); else if (ewin->state.active) opacity = ewin->props.focused_opacity; if (opacity == 0) opacity = ewin->props.opacity; if (opacity == 0) opacity = ewin->state.active ? OpacityFromPercent(Conf.opacity.focused) : OpacityFromPercent(Conf.opacity.unfocused); if (opacity == 0) opacity = 0xffffffff; /* Fallback */ EoChangeOpacity(ewin, opacity); } /* * Change requests */ static struct { unsigned int flags; Desk *desk; } EWinChanges; void EwinChange(EWin * ewin __UNUSED__, unsigned int flag) { EWinChanges.flags |= flag; } static void EwinChangesStart(EWin * ewin) { EWinChanges.flags = 0; EWinChanges.desk = EoGetDesk(ewin); } static 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 = EWinChanges.desk; 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) { EwinUpdateOpacity(ewin); HintsSetWindowOpacity(ewin); SnapshotEwinUpdate(ewin, SNAP_USE_OPACITY); } if (EWinChanges.flags & EWIN_CHANGE_ATTENTION) { HintsSetWindowState(ewin); } EWinChanges.flags = 0; } EWin ** EwinListTransients(const EWin * ewin, int *num, int group) { EWin *const *ewins, **lst, *ew; int i, j, n; j = 0; lst = NULL; if (EwinGetTransientCount(ewin) <= 0) goto done; ewins = EwinListGetAll(&n); /* Find regular transients */ for (i = 0; i < n; i++) { ew = ewins[i]; /* Skip self-reference */ if (ew == ewin) continue; if (EwinGetTransientFor(ew) == EwinGetClientXwin(ewin)) { lst = EREALLOC(EWin *, lst, j + 1); lst[j++] = ew; } } if (!group) goto done; /* Group transients (if ewin is not a transient) */ if (EwinIsTransient(ewin)) goto done; for (i = 0; i < n; i++) { ew = ewins[i]; /* Skip self-reference */ if (ew == ewin) continue; if (EwinGetTransientFor(ew) == WinGetXwin(VROOT) && EwinGetWindowGroup(ew) == EwinGetWindowGroup(ewin)) { lst = EREALLOC(EWin *, lst, j + 1); lst[j++] = ew; } } done: *num = j; return lst; } EWin ** EwinListTransientFor(const EWin * ewin, int *num) { EWin *const *ewins, **lst, *ew; int i, j, n; j = 0; lst = NULL; if (!EwinIsTransient(ewin)) goto done; ewins = EwinListGetAll(&n); for (i = 0; i < n; i++) { ew = ewins[i]; /* Skip self-reference */ if (ew == ewin) continue; /* Regular parent or if root trans, top level group members */ if ((EwinGetTransientFor(ewin) == EwinGetClientXwin(ew)) || (!EwinIsTransient(ew) && EwinGetTransientFor(ewin) == WinGetXwin(VROOT) && EwinGetWindowGroup(ew) == EwinGetWindowGroup(ewin))) { lst = EREALLOC(EWin *, lst, j + 1); lst[j++] = ew; } } done: *num = j; return lst; } 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 (EoIsMapped(ewin) && EwinIsOnScreen(ewin)) EwinMove(ewin, EoGetX(ewin), EoGetY(ewin), 0); } } static void EwinsReposition(void) { int i, num; EWin *const *lst; lst = EwinListGetAll(&num); for (i = num - 1; i >= 0; i--) EwinReposition(lst[i]); } 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 EwinsManage(void) { Window *xwins, par, rt; EX_Window xwin; XWindowAttributes attr; unsigned int i, num; #ifdef USE_EXT_INIT_WIN xwin = ExtInitWinGet(); if (xwin) XRaiseWindow(disp, xwin); #endif xwins = NULL; num = 0; XQueryTree(disp, WinGetXwin(VROOT), &rt, &par, &xwins, &num); if (!xwins) return; EGrabServer(); for (i = 0; i < num; i++) { xwin = xwins[i]; /* Skip if already "known" */ if (EobjListStackFind(xwin)) continue; if (!EXGetWindowAttributes(xwin, &attr)) continue; if (attr.map_state == IsUnmapped) continue; if (attr.override_redirect) EobjRegisterOR(xwin, &attr, 1); else AddToFamily(NULL, xwin, &attr, 1); } EUngrabServer(); XFree(xwins); } void EwinsSetFree(void) { int i, num; EWin *const *lst, *ewin; if (EDebug(EDBUG_TYPE_SESSION)) Eprintf("%s\n", __func__); EHintsSetInfoOnAll(); 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, ewin->client.x, ewin->client.y); } } /* * Event handlers */ #define DEBUG_EWIN_EVENTS 0 static int ActionsCheck(const char *which, EWin * ewin, XEvent * ev) { ActionClass *ac; ac = ActionclassFind(which); 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); } static EWin * _EwinEventEwinCheck(const char *txt, XEvent * ev, EWin * ewin) { int ser_diff; ser_diff = (int)(ev->xany.serial - ewin->serial); if (ser_diff < 0 && ser_diff > -1000) { Eprintf("%s: %#lx: Ignore obsolete event %d\n", txt, ev->xany.window, ev->type); return NULL; } ewin->serial = ev->xany.serial; return ewin; } static EWin * _EwinEventEwinFind(XEvent * ev, EX_Window xwin) { EWin *ewin; ewin = EwinFindByClient(xwin); if (!ewin) return ewin; return _EwinEventEwinCheck("root", ev, ewin); } static int EwinHandleContainerEvents(EWin * ewin, XEvent * ev) { EX_Window xwin = EwinGetClientXwin(ewin); switch (ev->type) { case MapRequest: if (ev->xmaprequest.window != xwin) break; EwinEventMapRequest(ewin, ev); break; case ConfigureRequest: if (ev->xconfigurerequest.window != xwin) break; EwinEventConfigureRequest(ewin, ev); break; case ResizeRequest: if (ev->xresizerequest.window != xwin) break; EwinEventResizeRequest(ewin, ev); break; case CirculateRequest: if (ev->xcirculaterequest.window != xwin) break; EwinEventCirculateRequest(ewin, ev); break; case DestroyNotify: if (ev->xdestroywindow.window != xwin) break; EwinEventDestroy(ewin); break; case EX_EVENT_UNMAP_GONE: if (ev->xunmap.window != xwin) break; EoSetGone(ewin); goto do_unmap; case UnmapNotify: if (ev->xunmap.window != xwin) break; do_unmap: EwinEventUnmap(ewin, ev); break; case MapNotify: if (ev->xmap.window != xwin) break; EwinEventMap(ewin, ev); break; case EX_EVENT_REPARENT_GONE: if (ev->xreparent.window != xwin) break; EoSetGone(ewin); goto do_reparent; case ReparentNotify: if (ev->xreparent.window != xwin) break; do_reparent: EwinEventReparent(ewin, ev); break; case EX_EVENT_MAP_GONE: case GravityNotify: case ConfigureNotify: break; default: return 0; /* Not handled */ } return 1; /* Handled */ } static void EwinHandleEventsToplevel(Win win __UNUSED__, XEvent * ev, void *prm) { EWin *ewin = (EWin *) prm; if (!_EwinEventEwinCheck("frm", ev, ewin)) return; 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; default: if (EwinHandleContainerEvents(ewin, ev)) break; #if DEBUG_EWIN_EVENTS Eprintf("%s: type=%2d win=%#lx: %s\n", __func__, ev->type, EwinGetClientXwin(ewin), EwinGetTitle(ewin)); #endif break; } } #if USE_CONTAINER_WIN static void EwinHandleEventsContainer(Win win __UNUSED__, XEvent * ev, void *prm) { EWin *ewin = (EWin *) prm; if (ev->type == ButtonPress) { FocusHandleClick(ewin, EwinGetClientConWin(ewin)); return; } if (!_EwinEventEwinCheck("cont", ev, ewin)) return; switch (ev->type) { default: if (EwinHandleContainerEvents(ewin, ev)) break; #if DEBUG_EWIN_EVENTS Eprintf("%s: type=%2d win=%#x: %s\n", __func__, ev->type, EwinGetClientXwin(ewin), EwinGetTitle(ewin)); #endif break; } } #endif static void EwinHandleEventsClient(Win win __UNUSED__, XEvent * ev, void *prm) { EWin *ewin = (EWin *) prm; #if !USE_CONTAINER_WIN if (ev->type == ButtonPress) { FocusHandleClick(ewin, EwinGetClientConWin(ewin)); return; } #endif if (!_EwinEventEwinCheck("cli", ev, ewin)) return; switch (ev->type) { case FocusIn: case FocusOut: if (ev->xfocus.detail == NotifyInferior) break; if (ewin->border->aclass) ActionclassEvent(ewin->border->aclass, ev, ewin); FocusHandleChange(ewin, ev); break; case ConfigureNotify: case GravityNotify: break; case VisibilityNotify: EwinEventVisibility(ewin, ev->xvisibility.state); break; case PropertyNotify: EwinEventPropertyNotify(ewin, ev); break; case ClientMessage: HintsProcessClientClientMessage(ewin, &(ev->xclient)); break; case EX_EVENT_SHAPE_NOTIFY: EwinEventShapeChange(ewin, ev); break; default: #if DEBUG_EWIN_EVENTS Eprintf("%s: type=%2d win=%#x: %s\n", __func__, ev->type, EwinGetClientXwin(ewin), EwinGetTitle(ewin)); #endif break; } } static void EwinHandleEventsRoot(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__) { EWin *ewin; switch (ev->type) { case MapRequest: EwinEventMapRequest(NULL, ev); break; case ConfigureRequest: #if 0 Eprintf("%s ConfigureRequest %#x\n", __func__, ev->xconfigurerequest.window); #endif ewin = EwinFindByClient(ev->xconfigurerequest.window); EwinEventConfigureRequest(ewin, ev); break; case ResizeRequest: #if 0 Eprintf("%s ResizeRequest %#x\n", __func__, ev->xresizerequest.window); #endif ewin = EwinFindByClient(ev->xresizerequest.window); EwinEventResizeRequest(ewin, ev); break; case CirculateRequest: #if 0 Eprintf("%s CirculateRequest %#x\n", __func__, ev->xcirculaterequest.window); #endif EwinEventCirculateRequest(NULL, ev); break; case UnmapNotify: case EX_EVENT_UNMAP_GONE: /* Catch clients unmapped after MapRequest but before being reparented */ ewin = _EwinEventEwinFind(ev, ev->xunmap.window); if (!ewin) break; if (ev->type == EX_EVENT_UNMAP_GONE) EoSetGone(ewin); EwinEventUnmap(ewin, ev); break; case DestroyNotify: /* Catch clients destroyed after MapRequest but before being reparented */ ewin = _EwinEventEwinFind(ev, ev->xdestroywindow.window); if (!ewin) break; EwinEventDestroy(ewin); break; case ReparentNotify: case EX_EVENT_REPARENT_GONE: ewin = _EwinEventEwinFind(ev, ev->xreparent.window); if (!ewin) break; if (ev->type == EX_EVENT_REPARENT_GONE) EoSetGone(ewin); EwinEventReparent(ewin, ev); break; case ClientMessage: HintsProcessRootClientMessage(&(ev->xclient)); break; default: #if 0 Eprintf("%s: type=%2d win=%#x\n", __func__, ev->type, ev->xany.window); #endif break; } } static void EwinsInit(void) { EventCallbackRegister(VROOT, 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_START: EwinsManage(); break; #endif case ESIGNAL_DESK_RESIZE: EwinsReposition(); break; case ESIGNAL_THEME_TRANS_CHANGE: EwinsTouch(DesksGetCurrent()); break; case ESIGNAL_BACKGROUND_CHANGE: EwinsTouch((Desk *) prm); break; } } /* * Module descriptor */ extern const EModule ModEwins; const EModule ModEwins = { "ewins", NULL, EwinsSighan, {0, NULL}, {0, NULL} };