diff options
Diffstat (limited to 'dwm.c')
| -rw-r--r-- | dwm.c | 4918 |
1 files changed, 4918 insertions, 0 deletions
@@ -0,0 +1,4918 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include <errno.h> +#include <locale.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/Xproto.h> +#include <X11/Xutil.h> +#ifdef XINERAMA +#include <X11/extensions/Xinerama.h> +#endif /* XINERAMA */ +#include <X11/Xft/Xft.h> + +#include "patches.h" +#include "drw.h" +#include "util.h" + +#if BAR_FLEXWINTITLE_PATCH +#ifndef FLEXTILE_DELUXE_LAYOUT +#define FLEXTILE_DELUXE_LAYOUT 1 +#endif +#endif + +#if BAR_PANGO_PATCH +#include <pango/pango.h> +#endif // BAR_PANGO_PATCH + +#if XKB_PATCH +#include <X11/XKBlib.h> +#endif // XKB_PATCH + +#if SPAWNCMD_PATCH +#include <assert.h> +#include <libgen.h> +#include <sys/stat.h> +#define SPAWN_CWD_DELIM " []{}()<>\"':" +#endif // SPAWNCMD_PATCH + +/* macros */ +#define Button6 6 +#define Button7 7 +#define Button8 8 +#define Button9 9 +#define NUMTAGS 9 +#define BARRULES 20 +#if TAB_PATCH +#define MAXTABS 50 +#endif // TAB_PATCH +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#if BAR_ANYBAR_PATCH +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->mx+(m)->mw) - MAX((x),(m)->mx)) \ + * MAX(0, MIN((y)+(h),(m)->my+(m)->mh) - MAX((y),(m)->my))) +#else +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#endif // BAR_ANYBAR_PATCH +#if ATTACHASIDE_PATCH && STICKY_PATCH +#define ISVISIBLEONTAG(C, T) ((C->tags & T) || C->issticky) +#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags]) +#elif ATTACHASIDE_PATCH +#define ISVISIBLEONTAG(C, T) ((C->tags & T)) +#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags]) +#elif STICKY_PATCH +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) +#else +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#endif // ATTACHASIDE_PATCH +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define WTYPE "_NET_WM_WINDOW_TYPE_" +#if SCRATCHPADS_PATCH +#define TOTALTAGS (NUMTAGS + LENGTH(scratchpads)) +#define TAGMASK ((1 << TOTALTAGS) - 1) +#define SPTAG(i) ((1 << NUMTAGS) << (i)) +#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << NUMTAGS) +#else +#define TAGMASK ((1 << NUMTAGS) - 1) +#endif // SCRATCHPADS_PATCH +#define TEXTWM(X) (drw_fontset_getwidth(drw, (X), True) + lrpad) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X), False) + lrpad) +#define HIDDEN(C) ((getstate(C->win) == IconicState)) + +/* enums */ +enum { + #if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH + CurResizeBR, + CurResizeBL, + CurResizeTR, + CurResizeTL, + #endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH + #if DRAGMFACT_PATCH + CurResizeHorzArrow, + CurResizeVertArrow, + #endif // DRAGMFACT_PATCH + #if DRAGCFACT_PATCH + CurIronCross, + #endif // DRAGCFACT_PATCH + CurNormal, + CurResize, + CurMove, + CurLast +}; /* cursor */ + +enum { + SchemeNorm, + SchemeSel, + SchemeTitleNorm, + SchemeTitleSel, + SchemeTagsNorm, + SchemeTagsSel, + SchemeHidNorm, + SchemeHidSel, + SchemeUrg, + #if BAR_FLEXWINTITLE_PATCH + SchemeFlexActTTB, + SchemeFlexActLTR, + SchemeFlexActMONO, + SchemeFlexActGRID, + SchemeFlexActGRD1, + SchemeFlexActGRD2, + SchemeFlexActGRDM, + SchemeFlexActHGRD, + SchemeFlexActDWDL, + SchemeFlexActSPRL, + SchemeFlexInaTTB, + SchemeFlexInaLTR, + SchemeFlexInaMONO, + SchemeFlexInaGRID, + SchemeFlexInaGRD1, + SchemeFlexInaGRD2, + SchemeFlexInaGRDM, + SchemeFlexInaHGRD, + SchemeFlexInaDWDL, + SchemeFlexInaSPRL, + SchemeFlexSelTTB, + SchemeFlexSelLTR, + SchemeFlexSelMONO, + SchemeFlexSelGRID, + SchemeFlexSelGRD1, + SchemeFlexSelGRD2, + SchemeFlexSelGRDM, + SchemeFlexSelHGRD, + SchemeFlexSelDWDL, + SchemeFlexSelSPRL, + SchemeFlexActFloat, + SchemeFlexInaFloat, + SchemeFlexSelFloat, + #endif // BAR_FLEXWINTITLE_PATCH +}; /* color schemes */ + +enum { + NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + #if BAR_WINICON_PATCH + NetWMIcon, + #endif // BAR_WINICON_PATCH + #if BAR_SYSTRAY_PATCH + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, + NetSystemTrayVisual, NetWMWindowTypeDock, NetSystemTrayOrientationHorz, + #endif // BAR_SYSTRAY_PATCH + #if BAR_EWMHTAGS_PATCH + NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, + #endif // BAR_EWMHTAGS_PATCH + NetClientList, + #if NET_CLIENT_LIST_STACKING_PATCH + NetClientListStacking, + #endif // NET_CLIENT_LIST_STACKING_PATCH + NetLast +}; /* EWMH atoms */ + +enum { + WMProtocols, + WMDelete, + WMState, + WMTakeFocus, + #if WINDOWROLERULE_PATCH + WMWindowRole, + #endif // WINDOWROLERULE_PATCH + WMLast +}; /* default atoms */ + +enum { + #if BAR_STATUSBUTTON_PATCH + ClkButton, + #endif // BAR_STATUSBUTTON_PATCH + #if TAB_PATCH + ClkTabBar, + #endif // TAB_PATCH + ClkTagBar, + ClkLtSymbol, + ClkStatusText, + ClkWinTitle, + ClkClientWin, + ClkRootWin, + #if XKB_PATCH + ClkXKB, + #endif // XKB_PATCH + ClkLast +}; /* clicks */ + +enum { + BAR_ALIGN_LEFT, + BAR_ALIGN_CENTER, + BAR_ALIGN_RIGHT, + BAR_ALIGN_LEFT_LEFT, + BAR_ALIGN_LEFT_RIGHT, + BAR_ALIGN_LEFT_CENTER, + BAR_ALIGN_NONE, + BAR_ALIGN_RIGHT_LEFT, + BAR_ALIGN_RIGHT_RIGHT, + BAR_ALIGN_RIGHT_CENTER, + BAR_ALIGN_LAST +}; /* bar alignment */ + +#if IPC_PATCH +typedef struct TagState TagState; +struct TagState { + int selected; + int occupied; + int urgent; +}; + +typedef struct ClientState ClientState; +struct ClientState { + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; +}; +#endif // IPC_PATCH + +typedef union { + #if IPC_PATCH + long i; + unsigned long ui; + #else + int i; + unsigned int ui; + #endif // IPC_PATCH + float f; + const void *v; +} Arg; + +typedef struct Monitor Monitor; +typedef struct Bar Bar; +struct Bar { + Window win; + Monitor *mon; + Bar *next; + int idx; + int showbar; + int topbar; + int external; + int borderpx; + int borderscheme; + int bx, by, bw, bh; /* bar geometry */ + int w[BARRULES]; // width, array length == barrules, then use r index for lookup purposes + int x[BARRULES]; // x position, array length == ^ +}; + +typedef struct { + int x; + int y; + int h; + int w; +} BarArg; + +typedef struct { + int monitor; + int bar; + int alignment; // see bar alignment enum + int (*widthfunc)(Bar *bar, BarArg *a); + int (*drawfunc)(Bar *bar, BarArg *a); + int (*clickfunc)(Bar *bar, Arg *arg, BarArg *a); + char *name; // for debugging + int x, w; // position, width for internal use +} BarRule; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +#if XKB_PATCH +typedef struct XkbInfo XkbInfo; +struct XkbInfo { + XkbInfo *next; + XkbInfo *prev; + int group; + Window w; +}; +#endif // XKB_PATCH + +typedef struct Client Client; +struct Client { + char name[256]; + float mina, maxa; + #if CFACTS_PATCH + float cfact; + #endif // CFACTS_PATCH + int x, y, w, h; + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */ + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + unsigned int tags; + #if SWITCHTAG_PATCH + unsigned int switchtag; + #endif // SWITCHTAG_PATCH + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + int fakefullscreen; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #if EXRESIZE_PATCH + unsigned char expandmask; + int expandx1, expandy1, expandx2, expandy2; + #if !MAXIMIZE_PATCH + int wasfloating; + #endif // MAXIMIZE_PATCH + #endif // EXRESIZE_PATCH + #if MAXIMIZE_PATCH + int ismax, wasfloating; + #endif // MAXIMIZE_PATCH + #if AUTORESIZE_PATCH + int needresize; + #endif // AUTORESIZE_PATCH + #if CENTER_PATCH + int iscentered; + #endif // CENTER_PATCH + #if ISPERMANENT_PATCH + int ispermanent; + #endif // ISPERMANENT_PATCH + #if PLACEMOUSE_PATCH + int beingmoved; + #endif // PLACEMOUSE_PATCH + #if SWALLOW_PATCH + int isterminal, noswallow; + pid_t pid; + #endif // SWALLOW_PATCH + #if STEAM_PATCH + int issteam; + #endif // STEAM_PATCH + #if STICKY_PATCH + int issticky; + #endif // STICKY_PATCH + Client *next; + Client *snext; + #if SWALLOW_PATCH + Client *swallowing; + #endif // SWALLOW_PATCH + Monitor *mon; + Window win; + #if IPC_PATCH + ClientState prevstate; + #endif // IPC_PATCH + #if XKB_PATCH + XkbInfo *xkb; + #endif // XKB_PATCH + #if BAR_WINICON_PATCH + XImage *icon; + #endif // BAR_WINICON_PATCH +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +#if FLEXTILE_DELUXE_LAYOUT +typedef struct { + int nmaster; + int nstack; + int layout; + int masteraxis; // master stack area + int stack1axis; // primary stack area + int stack2axis; // secondary stack area, e.g. centered master + void (*symbolfunc)(Monitor *, unsigned int); +} LayoutPreset; +#endif // FLEXTILE_DELUXE_LAYOUT + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); + #if FLEXTILE_DELUXE_LAYOUT + LayoutPreset preset; + #endif // FLEXTILE_DELUXE_LAYOUT +} Layout; + +#if INSETS_PATCH +typedef struct { + int x; + int y; + int w; + int h; +} Inset; +#endif // INSETS_PATCH + +#if PERTAG_PATCH +typedef struct Pertag Pertag; +#endif // PERTAG_PATCH +struct Monitor { + int index; + char ltsymbol[16]; + float mfact; + #if FLEXTILE_DELUXE_LAYOUT + int ltaxis[4]; + int nstack; + #endif // FLEXTILE_DELUXE_LAYOUT + int nmaster; + int num; + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + #if TAB_PATCH + int ty; /* tab bar geometry */ + #endif // TAB_PATCH + #if VANITYGAPS_PATCH + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ + #endif // VANITYGAPS_PATCH + #if SETBORDERPX_PATCH + unsigned int borderpx; + #endif // SETBORDERPX_PATCH + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + #if TAB_PATCH + int showtab; + int toptab; + Window tabwin; + int ntabs; + int tab_widths[MAXTABS]; + #endif // TAB_PATCH + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Bar *bar; + const Layout *lt[2]; + #if BAR_ALTERNATIVE_TAGS_PATCH + unsigned int alttag; + #endif // BAR_ALTERNATIVE_TAGS_PATCH + #if PERTAG_PATCH + Pertag *pertag; + #endif // PERTAG_PATCH + #if INSETS_PATCH + Inset inset; + #endif // INSETS_PATCH + #if IPC_PATCH + char lastltsymbol[16]; + TagState tagstate; + Client *lastsel; + const Layout *lastlt; + #endif // IPC_PATCH +}; + +typedef struct { + const char *class; + #if WINDOWROLERULE_PATCH + const char *role; + #endif // WINDOWROLERULE_PATCH + const char *instance; + const char *title; + const char *wintype; + unsigned int tags; + #if SWITCHTAG_PATCH + int switchtag; + #endif // SWITCHTAG_PATCH + #if CENTER_PATCH + int iscentered; + #endif // CENTER_PATCH + int isfloating; + #if SELECTIVEFAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + int isfakefullscreen; + #endif // SELECTIVEFAKEFULLSCREEN_PATCH + #if ISPERMANENT_PATCH + int ispermanent; + #endif // ISPERMANENT_PATCH + #if SWALLOW_PATCH + int isterminal; + int noswallow; + #endif // SWALLOW_PATCH + #if FLOATPOS_PATCH + const char *floatpos; + #endif // FLOATPOS_PATCH + int monitor; + #if XKB_PATCH + int xkb_layout; + #endif // XKB_PATCH +} Rule; + +#if XKB_PATCH +#define RULE(...) { .monitor = -1, .xkb_layout = -1, __VA_ARGS__ }, +#else +#define RULE(...) { .monitor = -1, __VA_ARGS__ }, +#endif // XKB_PATCH + +/* Cross patch compatibility rule macro helper macros */ +#define FLOATING , .isfloating = 1 +#if CENTER_PATCH +#define CENTERED , .iscentered = 1 +#else +#define CENTERED +#endif // CENTER_PATCH +#if ISPERMANENT_PATCH +#define PERMANENT , .ispermanent = 1 +#else +#define PERMANENT +#endif // ISPERMANENT_PATCH +#if SELECTIVEFAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH +#define FAKEFULLSCREEN , .isfakefullscreen = 1 +#else +#define FAKEFULLSCREEN +#endif // SELECTIVEFAKEFULLSCREEN_PATCH +#if SWALLOW_PATCH +#define NOSWALLOW , .noswallow = 1 +#define TERMINAL , .isterminal = 1 +#else +#define NOSWALLOW +#define TERMINAL +#endif // SWALLOW_PATCH +#if SWITCHTAG_PATCH +#define SWITCHTAG , .switchtag = 1 +#else +#define SWITCHTAG +#endif // SWITCHTAG_PATCH + +#if MONITOR_RULES_PATCH +typedef struct { + int monitor; + #if PERTAG_PATCH + int tag; + #endif // PERTAG_PATCH + int layout; + float mfact; + int nmaster; + int showbar; + int topbar; +} MonitorRule; +#endif // MONITOR_RULES_PATCH + +/* function declarations */ +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void drawbarwin(Bar *bar); +#if !FOCUSONCLICK_PATCH +static void enternotify(XEvent *e); +#endif // FOCUSONCLICK_PATCH +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +#if !STACKER_PATCH +static void focusstack(const Arg *arg); +#endif // STACKER_PATCH +static Atom getatomprop(Client *c, Atom prop); +static int getrootptr(int *x, int *y); +static long getstate(Window w); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +#if KEYMODES_PATCH +static void grabdefkeys(void); +#else +static void grabkeys(void); +#endif // KEYMODES_PATCH +static void incnmaster(const Arg *arg); +#if KEYMODES_PATCH +static void keydefpress(XEvent *e); +#else +static void keypress(XEvent *e); +#endif // KEYMODES_PATCH +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +#if !FOCUSONCLICK_PATCH +static void motionnotify(XEvent *e); +#endif // FOCUSONCLICK_PATCH +static void movemouse(const Arg *arg); +static Client *nexttiled(Client *c); +#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH +static void pop(Client *); +#endif // !ZOOMSWAP_PATCH / TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH +static void propertynotify(XEvent *e); +static void quit(const Arg *arg); +static Monitor *recttomon(int x, int y, int w, int h); +static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +#if BAR_SYSTRAY_PATCH +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); +#else +static int sendevent(Client *c, Atom proto); +#endif // BAR_SYSTRAY_PATCH +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setlayout(const Arg *arg); +static void setmfact(const Arg *arg); +static void setup(void); +static void seturgent(Client *c, int urg); +static void showhide(Client *c); +static void sigchld(int unused); +static void spawn(const Arg *arg); +#if RIODRAW_PATCH +static pid_t spawncmd(const Arg *arg); +#endif // RIODRAW_PATCH +static void tag(const Arg *arg); +static void tagmon(const Arg *arg); +static void togglebar(const Arg *arg); +static void togglefloating(const Arg *arg); +static void toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus, Client *nextfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +static void updatetitle(Client *c); +static void updatewmhints(Client *c); +static void view(const Arg *arg); +static Client *wintoclient(Window w); +static Monitor *wintomon(Window w); +static int xerror(Display *dpy, XErrorEvent *ee); +static int xerrordummy(Display *dpy, XErrorEvent *ee); +static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void zoom(const Arg *arg); + +/* bar functions */ + +#include "patch/include.h" + +/* variables */ +static const char broken[] = "broken"; +#if BAR_PANGO_PATCH || BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH +static char stext[1024]; +#else +static char stext[512]; +#endif // BAR_PANGO_PATCH | BAR_STATUS2D_PATCH +#if BAR_EXTRASTATUS_PATCH || BAR_STATUSCMD_PATCH +#if BAR_STATUS2D_PATCH +static char rawstext[1024]; +#else +static char rawstext[512]; +#endif // BAR_STATUS2D_PATCH +#endif // BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH +#if BAR_EXTRASTATUS_PATCH +#if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH +static char estext[1024]; +#else +static char estext[512]; +#endif // BAR_STATUS2D_PATCH +#if BAR_STATUSCMD_PATCH +static char rawestext[1024]; +#endif // BAR_STATUS2D_PATCH | BAR_STATUSCMD_PATCH +#endif // BAR_EXTRASTATUS_PATCH + +#if XKB_PATCH +static int xkbEventType = 0; +#endif // XKB_PATCH +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar geometry */ +static int lrpad; /* sum of left and right padding for text */ +/* Some clients (e.g. alacritty) helpfully send configure requests with a new size or position + * when they detect that they have been moved to another monitor. This can cause visual glitches + * when moving (or resizing) client windows from one monitor to another. This variable is used + * internally to ignore such configure requests while movemouse or resizemouse are being used. */ +static int ignoreconfigurerequests = 0; +#if WARP_PATCH +static int force_warp = 0; // force warp in some situations, e.g. killclient +static int ignore_warp = 0; // force skip warp in some situations, e.g. dragmfact, dragcfact +#endif // WARP_PATCH +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +#if RIODRAW_PATCH +static int riodimensions[4] = { -1, -1, -1, -1 }; +static pid_t riopid = 0; +#endif // RIODRAW_PATCH +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + #if COMBO_PATCH || BAR_HOLDBAR_PATCH + [ButtonRelease] = keyrelease, + #endif // COMBO_PATCH / BAR_HOLDBAR_PATCH + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + #if !FOCUSONCLICK_PATCH + [EnterNotify] = enternotify, + #endif // FOCUSONCLICK_PATCH + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + #if COMBO_PATCH || BAR_HOLDBAR_PATCH + [KeyRelease] = keyrelease, + #endif // COMBO_PATCH / BAR_HOLDBAR_PATCH + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + #if !FOCUSONCLICK_PATCH + [MotionNotify] = motionnotify, + #endif // FOCUSONCLICK_PATCH + [PropertyNotify] = propertynotify, + #if BAR_SYSTRAY_PATCH + [ResizeRequest] = resizerequest, + #endif // BAR_SYSTRAY_PATCH + [UnmapNotify] = unmapnotify +}; +#if BAR_SYSTRAY_PATCH +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; +#else +static Atom wmatom[WMLast], netatom[NetLast]; +#endif // BAR_SYSTRAY_PATCH +#if ON_EMPTY_KEYS_PATCH +static int isempty = 0; +#endif // ON_EMPTY_KEYS_PATCH +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* configuration, allows nested code to access above variables */ +#include "config.h" + +#include "patch/include.c" + +/* compile-time check if all tags fit into an unsigned int bit array. */ +#if SCRATCHPAD_ALT_1_PATCH +struct NumTags { char limitexceeded[NUMTAGS > 30 ? -1 : 1]; }; +#else +struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; }; +#endif // SCRATCHPAD_ALT_1_PATCH + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + Atom wintype; + #if WINDOWROLERULE_PATCH + char role[64]; + #endif // WINDOWROLERULE_PATCH + unsigned int i; + #if SWITCHTAG_PATCH + unsigned int newtagset; + #endif // SWITCHTAG_PATCH + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + #if SWALLOW_PATCH + c->noswallow = -1; + #endif // SWALLOW_PATCH + c->isfloating = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + wintype = getatomprop(c, netatom[NetWMWindowType]); + #if WINDOWROLERULE_PATCH + gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); + #endif // WINDOWROLERULE_PATCH + + #if STEAM_PATCH + if (strstr(class, "Steam") || strstr(class, "steam_app_")) + c->issteam = 1; + #endif // STEAM_PATCH + + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + #if WINDOWROLERULE_PATCH + && (!r->role || strstr(role, r->role)) + #endif // WINDOWROLERULE_PATCH + && (!r->instance || strstr(instance, r->instance)) + && (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False))) + { + #if CENTER_PATCH + c->iscentered = r->iscentered; + #endif // CENTER_PATCH + #if ISPERMANENT_PATCH + c->ispermanent = r->ispermanent; + #endif // ISPERMANENT_PATCH + #if SELECTIVEFAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + c->fakefullscreen = r->isfakefullscreen; + #endif // SELECTIVEFAKEFULLSCREEN_PATCH + #if SWALLOW_PATCH + c->isterminal = r->isterminal; + c->noswallow = r->noswallow; + #endif // SWALLOW_PATCH + c->isfloating = r->isfloating; + c->tags |= r->tags; + #if SCRATCHPADS_PATCH + if ((r->tags & SPTAGMASK) && r->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + #endif // SCRATCHPADS_PATCH + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + #if FLOATPOS_PATCH + if (c->isfloating && r->floatpos) { + #if CENTER_PATCH + c->iscentered = 0; + #endif // CENTER_PATCH + setfloatpos(c, r->floatpos); + } + #endif // FLOATPOS_PATCH + + #if SWITCHTAG_PATCH + #if SWALLOW_PATCH + if (r->switchtag && ( + c->noswallow > 0 || + !termforwin(c) || + !(c->isfloating && swallowfloating && c->noswallow < 0))) + #else + if (r->switchtag) + #endif // SWALLOW_PATCH + { + selmon = c->mon; + if (r->switchtag == 2 || r->switchtag == 4) + newtagset = c->mon->tagset[c->mon->seltags] ^ c->tags; + else + newtagset = c->tags; + + /* Switch to the client's tag, but only if that tag is not already shown */ + if (newtagset && !(c->tags & c->mon->tagset[c->mon->seltags])) { + if (r->switchtag == 3 || r->switchtag == 4) + c->switchtag = c->mon->tagset[c->mon->seltags]; + if (r->switchtag == 1 || r->switchtag == 3) { + #if PERTAG_PATCH + pertagview(&((Arg) { .ui = newtagset })); + arrange(c->mon); + #else + view(&((Arg) { .ui = newtagset })); + #endif // PERTAG_PATCH + } else { + c->mon->tagset[c->mon->seltags] = newtagset; + arrange(c->mon); + } + } + } + #endif // SWITCHTAG_PATCH + #if XKB_PATCH + if (r->xkb_layout > -1) + c->xkb->group = r->xkb_layout; + #endif // XKB_PATCH + #if ONLY_ONE_RULE_MATCH_PATCH + break; + #endif // ONLY_ONE_RULE_MATCH_PATCH + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + #if EMPTYVIEW_PATCH + if (c->tags & TAGMASK) c->tags = c->tags & TAGMASK; + #if SCRATCHPADS_PATCH + else if (c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags] & ~SPTAGMASK; + #elif SCRATCHPAD_ALT_1_PATCH + else if (c->tags != SCRATCHPAD_MASK && c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags]; + #else + else if (c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags]; + #endif // SCRATCHPADS_PATCH + else c->tags = 1; + #elif SCRATCHPADS_PATCH + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK); + #elif SCRATCHPAD_ALT_1_PATCH + if (c->tags != SCRATCHPAD_MASK) + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; + #else + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; + #endif // EMPTYVIEW_PATCH +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + #if TAB_PATCH + updatebarpos(m); + XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th); + #endif // TAB_PATCH + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); + #if ROUNDED_CORNERS_PATCH + Client *c; + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + drawroundedcorners(c); + #endif // ROUNDED_CORNERS_PATCH +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + int click, i, r; + #if TAB_PATCH + int x; + #endif // TAB_PATCH + Arg arg = {0}; + Client *c; + Monitor *m; + Bar *bar; + XButtonPressedEvent *ev = &e->xbutton; + const BarRule *br; + BarArg carg = { 0, 0, 0, 0 }; + click = ClkRootWin; + + #if BAR_STATUSCMD_PATCH && !BAR_DWMBLOCKS_PATCH + *lastbutton = '0' + ev->button; + #endif // BAR_STATUSCMD_PATCH | BAR_DWMBLOCKS_PATCH + + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon + #if FOCUSONCLICK_PATCH + && (focusonwheel || (ev->button != Button4 && ev->button != Button5)) + #endif // FOCUSONCLICK_PATCH + ) { + unfocus(selmon->sel, 1, NULL); + selmon = m; + focus(NULL); + } + + for (bar = selmon->bar; bar; bar = bar->next) { + if (ev->window == bar->win) { + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->index) + continue; + if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) { + carg.x = ev->x - bar->x[r]; + carg.y = ev->y - bar->borderpx; + carg.w = bar->w[r]; + carg.h = bar->bh - 2 * bar->borderpx; + click = br->clickfunc(bar, &arg, &carg); + if (click < 0) + return; + break; + } + } + break; + } + } + + #if TAB_PATCH + if (ev->window == selmon->tabwin) { + for (i = 0, x = 0, c = selmon->clients; c; c = c->next) { + if (!ISVISIBLE(c) || HIDDEN(c)) + continue; + x += selmon->tab_widths[i]; + if (ev->x > x) + ++i; + else + break; + if (i >= m->ntabs) + break; + } + if (c) { + click = ClkTabBar; + arg.ui = i; + } + } + #endif // TAB_PATCH + + if (click == ClkRootWin && (c = wintoclient(ev->window))) { + #if FOCUSONCLICK_PATCH + if (focusonwheel || (ev->button != Button4 && ev->button != Button5)) + focus(c); + #else + focus(c); + restack(selmon); + #endif // FOCUSONCLICK_PATCH + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + + for (i = 0; i < LENGTH(buttons); i++) { + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) { + buttons[i].func( + ( + click == ClkTagBar + #if TAB_PATCH + || click == ClkTabBar + #endif // TAB_PATCH + #if BAR_WINTITLEACTIONS_PATCH + || click == ClkWinTitle + #endif // BAR_WINTITLEACTIONS_PATCH + ) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg + ); + } + } +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + #if BAR_SYSTRAY_PATCH + if (showsystray && systray) { + while (systray->icons) + removesystrayicon(systray->icons); + if (systray->win) { + XUnmapWindow(dpy, systray->win); + XDestroyWindow(dpy, systray->win); + } + free(systray); + } + #endif // BAR_SYSTRAY_PATCH + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + #if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH + for (i = 0; i < LENGTH(colors) + 1; i++) + #else + for (i = 0; i < LENGTH(colors); i++) + #endif // BAR_STATUS2D_PATCH + free(scheme[i]); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + + #if IPC_PATCH + ipc_cleanup(); + + if (close(epoll_fd) < 0) + fprintf(stderr, "Failed to close epoll file descriptor\n"); + #endif // IPC_PATCH +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + Bar *bar; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + for (bar = mon->bar; bar; bar = mon->bar) { + if (!bar->external) { + XUnmapWindow(dpy, bar->win); + XDestroyWindow(dpy, bar->win); + } + mon->bar = bar->next; + #if BAR_SYSTRAY_PATCH + if (systray && bar == systray->bar) + systray->bar = NULL; + #endif // BAR_SYSTRAY_PATCH + free(bar); + } + #if TAB_PATCH + XUnmapWindow(dpy, mon->tabwin); + XDestroyWindow(dpy, mon->tabwin); + #endif // TAB_PATCH + #if PERTAG_PATCH + free(mon->pertag); + #endif // PERTAG_PATCH + free(mon); +} + +void +clientmessage(XEvent *e) +{ + #if BAR_SYSTRAY_PATCH + XWindowAttributes wa; + XSetWindowAttributes swa; + #endif // BAR_SYSTRAY_PATCH + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + #if FOCUSONNETACTIVE_PATCH + unsigned int i; + #endif // FOCUSONNETACTIVE_PATCH + + #if BAR_SYSTRAY_PATCH + if (showsystray && systray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { + /* add systray icons */ + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if (!(c = (Client *)calloc(1, sizeof(Client)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); + if (!(c->win = cme->data.l[2])) { + free(c); + return; + } + + c->mon = selmon; + c->next = systray->icons; + systray->icons = c; + XGetWindowAttributes(dpy, c->win, &wa); + c->x = c->oldx = c->y = c->oldy = 0; + c->w = c->oldw = wa.width; + c->h = c->oldh = wa.height; + c->oldbw = wa.border_width; + c->bw = 0; + c->isfloating = True; + /* reuse tags field as mapped status */ + c->tags = 1; + updatesizehints(c); + updatesystrayicongeom(c, wa.width, wa.height); + XAddToSaveSet(dpy, c->win); + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); + XClassHint ch = {"dwmsystray", "dwmsystray"}; + XSetClassHint(dpy, c->win, &ch); + XReparentWindow(dpy, c->win, systray->win, 0, 0); + /* use parents background color */ + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + XSync(dpy, False); + setclientstate(c, NormalState); + } + return; + } + #endif // BAR_SYSTRAY_PATCH + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) { + #if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + if (c->fakefullscreen == 2 && c->isfullscreen) + c->fakefullscreen = 3; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ + #if !FAKEFULLSCREEN_PATCH + && !c->isfullscreen + #endif // !FAKEFULLSCREEN_PATCH + ))); + } + } else if (cme->message_type == netatom[NetActiveWindow]) { + #if FOCUSONNETACTIVE_PATCH + if (c->tags & c->mon->tagset[c->mon->seltags]) + focus(c); + else { + for (i = 0; i < NUMTAGS && !((1 << i) & c->tags); i++); + if (i < NUMTAGS) { + if (c != selmon->sel) + unfocus(selmon->sel, 0, NULL); + selmon = c->mon; + if (((1 << i) & TAGMASK) != selmon->tagset[selmon->seltags]) + view(&((Arg) { .ui = 1 << i })); + focus(c); + restack(selmon); + } + } + #else + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + #endif // FOCUSONNETACTIVE_PATCH + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Bar *bar; + #if !FAKEFULLSCREEN_PATCH + Client *c; + #endif // !FAKEFULLSCREEN_PATCH + XConfigureEvent *ev = &e->xconfigure; + int dirty; + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, sh); + updatebars(); + for (m = mons; m; m = m->next) { + #if !FAKEFULLSCREEN_PATCH + for (c = m->clients; c; c = c->next) + #if FAKEFULLSCREEN_CLIENT_PATCH + if (c->isfullscreen && c->fakefullscreen != 1) + #else + if (c->isfullscreen) + #endif // FAKEFULLSCREEN_CLIENT_PATCH + resizeclient(c, m->mx, m->my, m->mw, m->mh); + #endif // !FAKEFULLSCREEN_PATCH + for (bar = m->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + #if BAR_ANYBAR_PATCH + Bar *bar; + #endif // BAR_ANYBAR_PATCH + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if (ignoreconfigurerequests) + return; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + #if STEAM_PATCH + if (!c->issteam) { + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + } + #else + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + #endif // STEAM_PATCH + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + #if AUTORESIZE_PATCH + else + c->needresize = 1; + #endif // AUTORESIZE_PATCH + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + #if BAR_ANYBAR_PATCH + m = wintomon(ev->window); + for (bar = m->bar; bar; bar = bar->next) { + if (bar->win == ev->window) { + wc.y = bar->by; + wc.x = bar->bx; + } + } + #endif // BAR_ANYBAR_PATCH + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m, *mon; + int i, n, mi, max_bars = 2, istopbar = topbar; + #if MONITOR_RULES_PATCH + int layout; + #endif // MONITOR_RULES_PATCH + + const BarRule *br; + Bar *bar; + #if MONITOR_RULES_PATCH + int j; + const MonitorRule *mr; + #endif // MONITOR_RULES_PATCH + + m = ecalloc(1, sizeof(Monitor)); + #if EMPTYVIEW_PATCH + m->tagset[0] = m->tagset[1] = 0; + #else + m->tagset[0] = m->tagset[1] = 1; + #endif // EMPTYVIEW_PATCH + m->mfact = mfact; + m->nmaster = nmaster; + #if FLEXTILE_DELUXE_LAYOUT + m->nstack = nstack; + #endif // FLEXTILE_DELUXE_LAYOUT + m->showbar = showbar; + #if TAB_PATCH + m->showtab = showtab; + m->toptab = toptab; + m->ntabs = 0; + #endif // TAB_PATCH + #if SETBORDERPX_PATCH + m->borderpx = borderpx; + #endif // SETBORDERPX_PATCH + #if VANITYGAPS_PATCH + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + #endif // VANITYGAPS_PATCH + for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index + m->index = mi; + #if MONITOR_RULES_PATCH + for (j = 0; j < LENGTH(monrules); j++) { + mr = &monrules[j]; + if ((mr->monitor == -1 || mr->monitor == mi) + #if PERTAG_PATCH + && (mr->tag <= 0 || (m->tagset[0] & (1 << (mr->tag - 1)))) + #endif // PERTAG_PATCH + ) { + layout = MAX(mr->layout, 0); + layout = MIN(layout, LENGTH(layouts) - 1); + m->lt[0] = &layouts[layout]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[layout].symbol, sizeof m->ltsymbol); + + if (mr->mfact > -1) + m->mfact = mr->mfact; + if (mr->nmaster > -1) + m->nmaster = mr->nmaster; + if (mr->showbar > -1) + m->showbar = mr->showbar; + if (mr->topbar > -1) + istopbar = mr->topbar; + break; + } + } + #else + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + #endif // MONITOR_RULES_PATCH + + /* Derive the number of bars for this monitor based on bar rules */ + for (n = -1, i = 0; i < LENGTH(barrules); i++) { + br = &barrules[i]; + if (br->monitor == 'A' || br->monitor == -1 || br->monitor == mi) + n = MAX(br->bar, n); + } + + m->bar = NULL; + for (i = 0; i <= n && i < max_bars; i++) { + bar = ecalloc(1, sizeof(Bar)); + bar->mon = m; + bar->idx = i; + bar->next = m->bar; + bar->topbar = istopbar; + m->bar = bar; + istopbar = !istopbar; + bar->showbar = 1; + bar->external = 0; + #if BAR_BORDER_PATCH + bar->borderpx = borderpx; + #else + bar->borderpx = 0; + #endif // BAR_BORDER_PATCH + bar->bh = bh + bar->borderpx * 2; + bar->borderscheme = SchemeNorm; + } + + #if FLEXTILE_DELUXE_LAYOUT + m->ltaxis[LAYOUT] = m->lt[0]->preset.layout; + m->ltaxis[MASTER] = m->lt[0]->preset.masteraxis; + m->ltaxis[STACK] = m->lt[0]->preset.stack1axis; + m->ltaxis[STACK2] = m->lt[0]->preset.stack2axis; + #endif // FLEXTILE_DELUXE_LAYOUT + + #if PERTAG_PATCH + if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + for (i = 0; i <= NUMTAGS; i++) { + #if FLEXTILE_DELUXE_LAYOUT + m->pertag->nstacks[i] = m->nstack; + #endif // FLEXTILE_DELUXE_LAYOUT + + #if !MONITOR_RULES_PATCH + /* init nmaster */ + m->pertag->nmasters[i] = m->nmaster; + + /* init mfacts */ + m->pertag->mfacts[i] = m->mfact; + + #if PERTAGBAR_PATCH + /* init showbar */ + m->pertag->showbars[i] = m->showbar; + #endif // PERTAGBAR_PATCH + #endif // MONITOR_RULES_PATCH + + #if ZOOMSWAP_PATCH + m->pertag->prevzooms[i] = NULL; + #endif // ZOOMSWAP_PATCH + + /* init layouts */ + #if MONITOR_RULES_PATCH + for (j = 0; j < LENGTH(monrules); j++) { + mr = &monrules[j]; + if ((mr->monitor == -1 || mr->monitor == mi) && (mr->tag == -1 || mr->tag == i)) { + layout = MAX(mr->layout, 0); + layout = MIN(layout, LENGTH(layouts) - 1); + m->pertag->ltidxs[i][0] = &layouts[layout]; + m->pertag->ltidxs[i][1] = m->lt[0]; + m->pertag->nmasters[i] = (mr->nmaster > -1 ? mr->nmaster : m->nmaster); + m->pertag->mfacts[i] = (mr->mfact > -1 ? mr->mfact : m->mfact); + #if PERTAGBAR_PATCH + m->pertag->showbars[i] = (mr->showbar > -1 ? mr->showbar : m->showbar); + #endif // PERTAGBAR_PATCH + #if FLEXTILE_DELUXE_LAYOUT + m->pertag->ltaxis[i][LAYOUT] = m->pertag->ltidxs[i][0]->preset.layout; + m->pertag->ltaxis[i][MASTER] = m->pertag->ltidxs[i][0]->preset.masteraxis; + m->pertag->ltaxis[i][STACK] = m->pertag->ltidxs[i][0]->preset.stack1axis; + m->pertag->ltaxis[i][STACK2] = m->pertag->ltidxs[i][0]->preset.stack2axis; + #endif // FLEXTILE_DELUXE_LAYOUT + break; + } + } + #else + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + #if FLEXTILE_DELUXE_LAYOUT + /* init flextile axes */ + m->pertag->ltaxis[i][LAYOUT] = m->ltaxis[LAYOUT]; + m->pertag->ltaxis[i][MASTER] = m->ltaxis[MASTER]; + m->pertag->ltaxis[i][STACK] = m->ltaxis[STACK]; + m->pertag->ltaxis[i][STACK2] = m->ltaxis[STACK2]; + #endif // FLEXTILE_DELUXE_LAYOUT + #endif // MONITOR_RULES_PATCH + m->pertag->sellts[i] = m->sellt; + + #if PERTAG_VANITYGAPS_PATCH && VANITYGAPS_PATCH + m->pertag->enablegaps[i] = 1; + m->pertag->gaps[i] = + ((gappoh & 0xFF) << 0) | ((gappov & 0xFF) << 8) | ((gappih & 0xFF) << 16) | ((gappiv & 0xFF) << 24); + #endif // PERTAG_VANITYGAPS_PATCH | VANITYGAPS_PATCH + } + #endif // PERTAG_PATCH + #if INSETS_PATCH + m->inset = default_inset; + #endif // INSETS_PATCH + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + #if BAR_ANYBAR_PATCH + Monitor *m; + Bar *bar; + #endif // BAR_ANYBAR_PATCH + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); + #if SWALLOW_PATCH + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); + #endif // SWALLOW_PATCH + #if BAR_SYSTRAY_PATCH + else if (showsystray && (c = wintosystrayicon(ev->window))) { + removesystrayicon(c); + drawbarwin(systray->bar); + } + #endif // BAR_SYSTRAY_PATCH + #if BAR_ANYBAR_PATCH + else { + m = wintomon(ev->window); + for (bar = m->bar; bar; bar = bar->next) { + if (bar->win == ev->window) { + unmanagealtbar(ev->window); + break; + } + } + } + #endif // BAR_ANYBAR_PATCH +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + Bar *bar; + for (bar = m->bar; bar; bar = bar->next) + drawbarwin(bar); +} + +void +drawbars(void) +{ + Monitor *m; + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +drawbarwin(Bar *bar) +{ + if (!bar || !bar->win || bar->external) + return; + int r, w, total_drawn = 0; + int rx, lx, rw, lw; // bar size, split between left and right if a center module is added + const BarRule *br; + + if (bar->borderpx) { + XSetForeground(drw->dpy, drw->gc, scheme[bar->borderscheme][ColBorder].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, 0, 0, bar->bw, bar->bh); + } + + BarArg warg = { 0 }; + BarArg darg = { 0 }; + warg.h = bar->bh - 2 * bar->borderpx; + + rw = lw = bar->bw - 2 * bar->borderpx; + rx = lx = bar->borderpx; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, lx, bar->borderpx, lw, bar->bh - 2 * bar->borderpx, 1, 1); + for (r = 0; r < LENGTH(barrules); r++) { + br = &barrules[r]; + if (br->bar != bar->idx || !br->widthfunc || (br->monitor == 'A' && bar->mon != selmon)) + continue; + if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->index) + continue; + drw_setscheme(drw, scheme[SchemeNorm]); + warg.w = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw); + + w = br->widthfunc(bar, &warg); + w = MIN(warg.w, w); + + if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa + lw = rw; + lx = rx; + } else if (rw <= 0) { + rw = lw; + rx = lx; + } + + switch(br->alignment) { + default: + case BAR_ALIGN_NONE: + case BAR_ALIGN_LEFT_LEFT: + case BAR_ALIGN_LEFT: + bar->x[r] = lx; + if (lx == rx) { + rx += w; + rw -= w; + } + lx += w; + lw -= w; + break; + case BAR_ALIGN_LEFT_RIGHT: + case BAR_ALIGN_RIGHT: + bar->x[r] = lx + lw - w; + if (lx == rx) + rw -= w; + lw -= w; + break; + case BAR_ALIGN_LEFT_CENTER: + case BAR_ALIGN_CENTER: + bar->x[r] = lx + lw / 2 - w / 2; + if (lx == rx) { + rw = rx + rw - bar->x[r] - w; + rx = bar->x[r] + w; + } + lw = bar->x[r] - lx; + break; + case BAR_ALIGN_RIGHT_LEFT: + bar->x[r] = rx; + if (lx == rx) { + lx += w; + lw -= w; + } + rx += w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_RIGHT: + bar->x[r] = rx + rw - w; + if (lx == rx) + lw -= w; + rw -= w; + break; + case BAR_ALIGN_RIGHT_CENTER: + bar->x[r] = rx + rw / 2 - w / 2; + if (lx == rx) { + lw = lx + lw - bar->x[r] + w; + lx = bar->x[r] + w; + } + rw = bar->x[r] - rx; + break; + } + bar->w[r] = w; + darg.x = bar->x[r]; + darg.y = bar->borderpx; + darg.h = bar->bh - 2 * bar->borderpx; + darg.w = bar->w[r]; + if (br->drawfunc) + total_drawn += br->drawfunc(bar, &darg); + } + + if (total_drawn == 0 && bar->showbar) { + bar->showbar = 0; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(bar->mon); + } + else if (total_drawn > 0 && !bar->showbar) { + bar->showbar = 1; + updatebarpos(bar->mon); + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); + arrange(bar->mon); + } else + drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh); +} + +#if !FOCUSONCLICK_PATCH +void +enternotify(XEvent *e) +{ + Client *c; + #if LOSEFULLSCREEN_PATCH + Client *sel; + #endif // LOSEFULLSCREEN_PATCH + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + #if LOSEFULLSCREEN_PATCH + sel = selmon->sel; + selmon = m; + unfocus(sel, 1, c); + #else + unfocus(selmon->sel, 1, c); + selmon = m; + #endif // LOSEFULLSCREEN_PATCH + } else if (!c || c == selmon->sel) + return; + focus(c); +} +#endif // FOCUSONCLICK_PATCH + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) { + drawbar(m); + #if TAB_PATCH + drawtabs(); + #endif // TAB_PATCH + } +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0, c); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + #if !BAR_FLEXWINTITLE_PATCH + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + #endif // BAR_FLEXWINTITLE_PATCH + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); + + #if ON_EMPTY_KEYS_PATCH + if ((isempty && selmon->sel) || (!isempty && !selmon->sel)) { + isempty = !isempty; + grabkeys(); + } + #endif // ON_EMPTY_KEYS_PATCH +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + #if LOSEFULLSCREEN_PATCH + Client *sel; + #endif // LOSEFULLSCREEN_PATCH + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + #if LOSEFULLSCREEN_PATCH + sel = selmon->sel; + selmon = m; + unfocus(sel, 0, NULL); + #else + unfocus(selmon->sel, 0, NULL); + selmon = m; + #endif // LOSEFULLSCREEN_PATCH + focus(NULL); + #if WARP_PATCH + warp(selmon->sel); + #endif // WARP_PATCH +} + +#if !STACKER_PATCH +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + + #if LOSEFULLSCREEN_PATCH + if (!selmon->sel) + return; + #elif FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + if (!selmon->sel || (selmon->sel->isfullscreen && !selmon->sel->fakefullscreen)) + return; + #else + if (!selmon->sel || selmon->sel->isfullscreen) + return; + #endif // LOSEFULLSCREEN_PATCH + #if BAR_WINTITLEACTIONS_PATCH + if (arg->i > 0) { + for (c = selmon->sel->next; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + if (!c) + for (c = selmon->clients; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i))) + c = i; + } + #else + if (arg->i > 0) { + for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!c) + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + #endif // BAR_WINTITLEACTIONS_PATCH + if (c) { + focus(c); + restack(selmon); + } +} +#endif // STACKER_PATCH + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + #if BAR_SYSTRAY_PATCH + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + Atom req = XA_ATOM; + if (prop == xatom[XembedInfo]) + req = xatom[XembedInfo]; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + if (da == xatom[XembedInfo] && dl == 2) + atom = ((Atom *)p)[1]; + XFree(p); + } + #else + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + #endif // BAR_SYSTRAY_PATCH + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) + strncpy(text, (char *)name.value, size - 1); + else { + if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin + #if NO_MOD_BUTTONS_PATCH + && (nomodbuttons || buttons[i].mask != 0) + #endif // NO_MOD_BUTTONS_PATCH + ) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +#if KEYMODES_PATCH +grabdefkeys(void) +#else +grabkeys(void) +#endif // KEYMODES_PATCH +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + #if ON_EMPTY_KEYS_PATCH + if (!selmon->sel) + for (i = 0; i < LENGTH(on_empty_keys); i++) + if ((code = XKeysymToKeycode(dpy, on_empty_keys[i].keysym))) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, on_empty_keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + #endif // ON_EMPTY_KEYS_PATCH + } +} + +void +incnmaster(const Arg *arg) +{ + #if PERTAG_PATCH + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + #else + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + #endif // PERTAG_PATCH + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +#if KEYMODES_PATCH +keydefpress(XEvent *e) +#else +keypress(XEvent *e) +#endif // KEYMODES_PATCH +{ + unsigned int i; + int keysyms_return; + KeySym* keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XGetKeyboardMapping(dpy, (KeyCode)ev->keycode, 1, &keysyms_return); + for (i = 0; i < LENGTH(keys); i++) + if (*keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); + #if ON_EMPTY_KEYS_PATCH + if (!selmon->sel) + for (i = 0; i < LENGTH(on_empty_keys); i++) + if (*keysym == on_empty_keys[i].keysym + && CLEANMASK(on_empty_keys[i].mod) == CLEANMASK(ev->state) + && on_empty_keys[i].func) + on_empty_keys[i].func(&(on_empty_keys[i].arg)); + #endif // ON_EMPTY_KEYS_PATCH + XFree(keysym); +} + +void +killclient(const Arg *arg) +{ + #if ISPERMANENT_PATCH + if (!selmon->sel || selmon->sel->ispermanent) + #else + if (!selmon->sel) + #endif // ISPERMANENT_PATCH + return; + #if BAR_SYSTRAY_PATCH + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) + #else + if (!sendevent(selmon->sel, wmatom[WMDelete])) + #endif // BAR_SYSTRAY_PATCH + { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + #if WARP_PATCH + force_warp = 1; + #endif // WARP_PATCH + } + #if SWAPFOCUS_PATCH && PERTAG_PATCH + selmon->pertag->prevclient[selmon->pertag->curtag] = NULL; + #endif // SWAPFOCUS_PATCH +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + #if SWALLOW_PATCH + Client *term = NULL; + #endif // SWALLOW_PATCH + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + #if SWALLOW_PATCH + c->pid = winpid(w); + #endif // SWALLOW_PATCH + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + #if CFACTS_PATCH + c->cfact = 1.0; + #endif // CFACTS_PATCH + #if BAR_WINICON_PATCH + c->icon = NULL; + updateicon(c); + #endif // BAR_WINICON_PATCH + updatetitle(c); + + #if XKB_PATCH + /* Setting current xkb state must be before applyrules */ + if (!(c->xkb = findxkb(c->win))) + c->xkb = createxkb(c->win); + #endif // XKB_PATCH + + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + #if SETBORDERPX_PATCH + c->bw = c->mon->borderpx; + #else + c->bw = borderpx; + #endif // SETBORDERPX_PATCH + #if CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH + c->x = t->x + WIDTH(t) / 2 - WIDTH(c) / 2; + c->y = t->y + HEIGHT(t) / 2 - HEIGHT(c) / 2; + #elif CENTER_PATCH && CENTER_TRANSIENT_WINDOWS_PATCH + c->iscentered = 1; + #elif CENTER_TRANSIENT_WINDOWS_PATCH + c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; + c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; + #elif CENTER_PATCH + if (c->x == c->mon->wx && c->y == c->mon->wy) + c->iscentered = 1; + #endif // CENTER_TRANSIENT_WINDOWS_PATCH | CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH | CENTER_PATCH + } else { + c->mon = selmon; + #if CENTER_PATCH + if (c->x == c->mon->wx && c->y == c->mon->wy) + c->iscentered = 1; + #endif // CENTER_PATCH + #if SETBORDERPX_PATCH + c->bw = c->mon->borderpx; + #else + c->bw = borderpx; + #endif // SETBORDERPX_PATCH + applyrules(c); + #if SWALLOW_PATCH + term = termforwin(c); + if (term) + c->mon = term->mon; + #endif // SWALLOW_PATCH + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) + c->x = c->mon->mx + c->mon->mw - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((!c->mon->bar || c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + #if !BAR_FLEXWINTITLE_PATCH + if (c->isfloating) + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + else + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + #endif // BAR_FLEXWINTITLE_PATCH + configure(c); /* propagates border_width, if size doesn't change */ + updatesizehints(c); + if (getatomprop(c, netatom[NetWMState]) == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + updatewmhints(c); + #if DECORATION_HINTS_PATCH + updatemotifhints(c); + #endif // DECORATION_HINTS_PATCH + #if CENTER_PATCH + if (c->iscentered) { + c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; + c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; + } + #endif // CENTER_PATCH + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + c->sfx = -9999; + c->sfy = -9999; + c->sfw = c->w; + c->sfh = c->h; + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + #if MAXIMIZE_PATCH + c->wasfloating = 0; + c->ismax = 0; + #elif EXRESIZE_PATCH + c->wasfloating = 0; + #endif // MAXIMIZE_PATCH / EXRESIZE_PATCH + + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) { + XRaiseWindow(dpy, c->win); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); + } + #if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH + attachx(c); + #else + attach(c); + #endif + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + #if NET_CLIENT_LIST_STACKING_PATCH + XChangeProperty(dpy, root, netatom[NetClientListStacking], XA_WINDOW, 32, PropModePrepend, + (unsigned char *) &(c->win), 1); + #endif // NET_CLIENT_LIST_STACKING_PATCH + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + + #if BAR_WINTITLEACTIONS_PATCH + if (!HIDDEN(c)) + setclientstate(c, NormalState); + #else + setclientstate(c, NormalState); + #endif // BAR_WINTITLEACTIONS_PATCH + if (c->mon == selmon) + unfocus(selmon->sel, 0, c); + c->mon->sel = c; + #if SWALLOW_PATCH + if (!(term && swallow(term, c))) { + #if RIODRAW_PATCH + if (riopid && (!riodraw_matchpid || isdescprocess(riopid, c->pid))) { + if (riodimensions[3] != -1) + rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); + else { + killclient(&((Arg) { .v = c })); + return; + } + } + #endif // RIODRAW_PATCH + arrange(c->mon); + #if BAR_WINTITLEACTIONS_PATCH + if (!HIDDEN(c)) + XMapWindow(dpy, c->win); + #else + XMapWindow(dpy, c->win); + #endif // BAR_WINTITLEACTIONS_PATCH + } + #else + #if RIODRAW_PATCH + if (riopid) { + if (riodimensions[3] != -1) + rioposition(c, riodimensions[0], riodimensions[1], riodimensions[2], riodimensions[3]); + else { + killclient(&((Arg) { .v = c })); + return; + } + } + #endif // RIODRAW_PATCH + arrange(c->mon); + #if BAR_WINTITLEACTIONS_PATCH + if (!HIDDEN(c)) + XMapWindow(dpy, c->win); + #else + XMapWindow(dpy, c->win); + #endif // BAR_WINTITLEACTIONS_PATCH + #endif // SWALLOW_PATCH + focus(NULL); + + #if BAR_EWMHTAGS_PATCH + setfloatinghint(c); + #endif // BAR_EWMHTAGS_PATCH +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + #if BAR_SYSTRAY_PATCH + Client *i; + if (showsystray && systray && (i = wintosystrayicon(ev->window))) { + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); + drawbarwin(systray->bar); + } + #endif // BAR_SYSTRAY_PATCH + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) + return; + #if BAR_ANYBAR_PATCH + if (wmclasscontains(ev->window, altbarclass, "")) + managealtbar(ev->window, &wa); + else + #endif // BAR_ANYBAR_PATCH + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +#if !FOCUSONCLICK_PATCH +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + #if LOSEFULLSCREEN_PATCH + Client *sel; + #endif // LOSEFULLSCREEN_PATCH + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + #if LOSEFULLSCREEN_PATCH + sel = selmon->sel; + selmon = m; + unfocus(sel, 1, NULL); + #else + unfocus(selmon->sel, 1, NULL); + selmon = m; + #endif // LOSEFULLSCREEN_PATCH + focus(NULL); + } + mon = m; +} +#endif // FOCUSONCLICK_PATCH + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + #if !FAKEFULLSCREEN_PATCH + #if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + if (c->isfullscreen && c->fakefullscreen != 1) /* no support moving fullscreen windows by mouse */ + return; + #else + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #endif // FAKEFULLSCREEN_PATCH + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + ignoreconfigurerequests = 1; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) { + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + c->sfx = -9999; // disable savefloats when using movemouse + #endif // SAVEFLOATS_PATCH | EXRESIZE_PATCH + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + resize(c, nx, ny, c->w, c->h, 1); + /* save last known float coordinates */ + c->sfx = nx; + c->sfy = ny; + #else + resize(c, nx, ny, c->w, c->h, 1); + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + } + #if ROUNDED_CORNERS_PATCH + drawroundedcorners(c); + #endif // ROUNDED_CORNERS_PATCH + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + #if SCRATCHPADS_PATCH + if (c->tags & SPTAGMASK) { + c->mon->tagset[c->mon->seltags] ^= (c->tags & SPTAGMASK); + m->tagset[m->seltags] |= (c->tags & SPTAGMASK); + } + #endif // SCRATCHPADS_PATCH + sendmon(c, m); + selmon = m; + focus(NULL); + } + #if ROUNDED_CORNERS_PATCH + drawroundedcorners(c); + #endif // ROUNDED_CORNERS_PATCH + ignoreconfigurerequests = 0; +} + +Client * +nexttiled(Client *c) +{ + #if BAR_WINTITLEACTIONS_PATCH + for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); + #else + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + #endif // BAR_WINTITLEACTIONS_PATCH + return c; +} + +#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} +#endif // !ZOOMSWAP_PATCH / TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + #if BAR_SYSTRAY_PATCH + if (showsystray && (c = wintosystrayicon(ev->window))) { + if (ev->atom == XA_WM_NORMAL_HINTS) { + updatesizehints(c); + updatesystrayicongeom(c, c->w, c->h); + } + else + updatesystrayiconstate(c, ev); + drawbarwin(systray->bar); + } + #endif // BAR_SYSTRAY_PATCH + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) { + #if DWMC_PATCH || FSIGNAL_PATCH + if (!fake_signal()) + updatestatus(); + #else + updatestatus(); + #endif // DWMC_PATCH / FSIGNAL_PATCH + } else if (ev->state == PropertyDelete) { + return; /* ignore */ + } else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + case XA_WM_HINTS: + updatewmhints(c); + if (c->isurgent) + drawbars(); + #if TAB_PATCH + drawtabs(); + #endif // TAB_PATCH + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + #if TAB_PATCH + drawtab(c->mon); + #endif // TAB_PATCH + } + #if DECORATION_HINTS_PATCH + if (ev->atom == motifatom) + updatemotifhints(c); + #endif // DECORATION_HINTS_PATCH + #if BAR_WINICON_PATCH + else if (ev->atom == netatom[NetWMIcon]) { + updateicon(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + #endif // BAR_WINICON_PATCH + } +} + +void +quit(const Arg *arg) +{ + #if COOL_AUTOSTART_PATCH + size_t i; + #endif // COOL_AUTOSTART_PATCH + #if ONLYQUITONEMPTY_PATCH + unsigned int n; + Window *junk = malloc(1); + + XQueryTree(dpy, root, junk, junk, &junk, &n); + + #if COOL_AUTOSTART_PATCH + if (n - autostart_len <= quit_empty_window_count) + #else + if (n <= quit_empty_window_count) + #endif // COOL_AUTOSTART_PATCH + { + #if RESTARTSIG_PATCH + if (arg->i) + restart = 1; + #endif // RESTARTSIG_PATCH + running = 0; + } + else + printf("[dwm] not exiting (n=%d)\n", n); + + free(junk); + #else + #if RESTARTSIG_PATCH + if (arg->i) + restart = 1; + #endif // RESTARTSIG_PATCH + running = 0; + #endif // ONLYQUITONEMPTY_PATCH + + #if COOL_AUTOSTART_PATCH + /* kill child processes */ + for (i = 0; i < autostart_len; i++) { + if (0 < autostart_pids[i]) { + kill(autostart_pids[i], SIGTERM); + waitpid(autostart_pids[i], NULL, 0); + } + } + #endif // COOL_AUTOSTART_PATCH +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + #if EXRESIZE_PATCH + c->expandmask = 0; + #endif // EXRESIZE_PATCH + wc.border_width = c->bw; + #if NOBORDER_PATCH + if (((nexttiled(c->mon->clients) == c && !nexttiled(c->next)) + #if MONOCLE_LAYOUT + || &monocle == c->mon->lt[c->mon->sellt]->arrange + #endif // MONOCLE_LAYOUT + #if DECK_LAYOUT + || (&deck == c->mon->lt[c->mon->sellt]->arrange && + c->mon->nmaster == 0) + #endif // DECK_LAYOUT + #if FLEXTILE_DELUXE_LAYOUT + || (&flextile == c->mon->lt[c->mon->sellt]->arrange && ( + (c->mon->ltaxis[LAYOUT] == NO_SPLIT && + c->mon->ltaxis[MASTER] == MONOCLE) || + (c->mon->ltaxis[STACK] == MONOCLE && + c->mon->nmaster == 0))) + #endif //FLEXTILE_DELUXE_LAYOUT + ) + #if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH + && (c->fakefullscreen == 1 || !c->isfullscreen) + #else + && !c->isfullscreen + #endif // FAKEFULLSCREEN_CLIENT_PATCH + && !c->isfloating + && c->mon->lt[c->mon->sellt]->arrange) { + wc.width += c->bw * 2; + wc.height += c->bw * 2; + wc.border_width = 0; + } + #endif // NOBORDER_PATCH + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + #if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH + int opx, opy, och, ocw, nx, ny; + int horizcorner, vertcorner; + unsigned int dui; + Window dummy; + #endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + #if !FAKEFULLSCREEN_PATCH + #if FAKEFULLSCREEN_CLIENT_PATCH + if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */ + return; + #else + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #endif // !FAKEFULLSCREEN_PATCH + restack(selmon); + ocx = c->x; + ocy = c->y; + #if RESIZEPOINT_PATCH + och = c->h; + ocw = c->w; + #elif RESIZECORNERS_PATCH + och = c->y + c->h; + ocw = c->x + c->w; + #endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH + #if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH + if (!XQueryPointer(dpy, c->win, &dummy, &dummy, &opx, &opy, &nx, &ny, &dui)) + return; + horizcorner = nx < c->w / 2; + vertcorner = ny < c->h / 2; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[horizcorner | (vertcorner << 1)]->cursor, CurrentTime) != GrabSuccess) + return; + #if RESIZECORNERS_PATCH + XWarpPointer (dpy, None, c->win, 0, 0, 0, 0, + horizcorner ? (-c->bw) : (c->w + c->bw - 1), + vertcorner ? (-c->bw) : (c->h + c->bw - 1)); + #endif // RESIZECORNERS_PATCH + #else + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + #endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH + ignoreconfigurerequests = 1; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + #if RESIZEPOINT_PATCH + nx = horizcorner ? (ocx + ev.xmotion.x - opx) : c->x; + ny = vertcorner ? (ocy + ev.xmotion.y - opy) : c->y; + nw = MAX(horizcorner ? (ocx + ocw - nx) : (ocw + (ev.xmotion.x - opx)), 1); + nh = MAX(vertcorner ? (ocy + och - ny) : (och + (ev.xmotion.y - opy)), 1); + #elif RESIZECORNERS_PATCH + nx = horizcorner ? ev.xmotion.x : c->x; + ny = vertcorner ? ev.xmotion.y : c->y; + nw = MAX(horizcorner ? (ocw - nx) : (ev.xmotion.x - ocx - 2 * c->bw + 1), 1); + nh = MAX(vertcorner ? (och - ny) : (ev.xmotion.y - ocy - 2 * c->bw + 1), 1); + #else + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + #endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) { + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + c->sfx = -9999; // disable savefloats when using resizemouse + #endif // SAVEFLOATS_PATCH | EXRESIZE_PATCH + togglefloating(NULL); + } + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + #if RESIZECORNERS_PATCH || RESIZEPOINT_PATCH + resizeclient(c, nx, ny, nw, nh); + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + /* save last known float dimensions */ + c->sfx = nx; + c->sfy = ny; + c->sfw = nw; + c->sfh = nh; + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + #else + resize(c, c->x, c->y, nw, nh, 1); + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + c->sfx = c->x; + c->sfy = c->y; + c->sfw = nw; + c->sfh = nh; + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + #endif // RESIZECORNERS_PATCH + #if ROUNDED_CORNERS_PATCH + drawroundedcorners(c); + #endif // ROUNDED_CORNERS_PATCH + } + break; + } + } while (ev.type != ButtonRelease); + #if !RESIZEPOINT_PATCH + #if RESIZECORNERS_PATCH + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, + horizcorner ? (-c->bw) : (c->w + c->bw - 1), + vertcorner ? (-c->bw) : (c->h + c->bw - 1)); + #else + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + #endif // RESIZECORNERS_PATCH + #endif // RESIZEPOINT_PATCH + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + #if SCRATCHPADS_PATCH + if (c->tags & SPTAGMASK) { + c->mon->tagset[c->mon->seltags] ^= (c->tags & SPTAGMASK); + m->tagset[m->seltags] |= (c->tags & SPTAGMASK); + } + #endif // SCRATCHPADS_PATCH + sendmon(c, m); + selmon = m; + focus(NULL); + } + ignoreconfigurerequests = 0; +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + #if WARP_PATCH && FLEXTILE_DELUXE_LAYOUT + int n; + #endif // WARP_PATCH + + drawbar(m); + #if TAB_PATCH + drawtab(m); + #endif // TAB_PATCH + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange && m->bar) { + wc.stack_mode = Below; + wc.sibling = m->bar->win; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + #if WARP_PATCH && FLEXTILE_DELUXE_LAYOUT || WARP_PATCH && MONOCLE_LAYOUT + #if FLEXTILE_DELUXE_LAYOUT + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + #endif // FLEXTILE_DELUXE_LAYOUT + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && ( + #if MONOCLE_LAYOUT && FLEXTILE_DELUXE_LAYOUT + (m->lt[m->sellt]->arrange != &monocle + && !(m->ltaxis[MASTER] == MONOCLE && (abs(m->ltaxis[LAYOUT] == NO_SPLIT || !m->nmaster || n <= m->nmaster)))) + #elif MONOCLE_LAYOUT + m->lt[m->sellt]->arrange != &monocle + #else + !(m->ltaxis[MASTER] == MONOCLE && (abs(m->ltaxis[LAYOUT] == NO_SPLIT || !m->nmaster || n <= m->nmaster))) + #endif // FLEXTILE_DELUXE_LAYOUT + || m->sel->isfloating) + ) + warp(m->sel); + #endif // WARP_PATCH +} + +#if IPC_PATCH +void +run(void) +{ + int event_count = 0; + const int MAX_EVENTS = 10; + struct epoll_event events[MAX_EVENTS]; + + XSync(dpy, False); + + /* main event loop */ + while (running) { + event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); + + for (int i = 0; i < event_count; i++) { + int event_fd = events[i].data.fd; + DEBUG("Got event from fd %d\n", event_fd); + + if (event_fd == dpy_fd) { + // -1 means EPOLLHUP + if (handlexevent(events + i) == -1) + return; + } else if (event_fd == ipc_get_sock_fd()) { + ipc_handle_socket_epoll_event(events + i); + } else if (ipc_is_client_registered(event_fd)) { + if (ipc_handle_client_epoll_event(events + i, mons, &lastselmon, selmon, + NUMTAGS, layouts, LENGTH(layouts)) < 0) { + fprintf(stderr, "Error handling IPC event on fd %d\n", event_fd); + } + } else { + fprintf(stderr, "Got event from unknown fd %d, ptr %p, u32 %d, u64 %lu", + event_fd, events[i].data.ptr, events[i].data.u32, + events[i].data.u64); + fprintf(stderr, " with events %d\n", events[i].events); + return; + } + } + } +} +#else +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) { + + #if XKB_PATCH + /* Unfortunately the xkbEventType is not constant hence it can't be part of the + * normal event handler below */ + if (ev.type == xkbEventType) { + xkbeventnotify(&ev); + continue; + } + #endif // XKB_PATCH + + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ + } +} +#endif // IPC_PATCH + +void +scan(void) +{ + #if SWALLOW_PATCH + scanner = 1; + char swin[256]; + #endif // SWALLOW_PATCH + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + #if BAR_ANYBAR_PATCH + if (wmclasscontains(wins[i], altbarclass, "")) + managealtbar(wins[i], &wa); + else + #endif // BAR_ANYBAR_PATCH + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + #if SWALLOW_PATCH + else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) + manage(wins[i], &wa); + #endif // SWALLOW_PATCH + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + XFree(wins); + } + #if SWALLOW_PATCH + scanner = 0; + #endif // SWALLOW_PATCH +} + +void +sendmon(Client *c, Monitor *m) +{ + #if EXRESIZE_PATCH + Monitor *oldm = selmon; + #endif // EXRESIZE_PATCH + if (c->mon == m) + return; + #if SENDMON_KEEPFOCUS_PATCH && !EXRESIZE_PATCH + int hadfocus = (c == selmon->sel); + #endif // SENDMON_KEEPFOCUS_PATCH + unfocus(c, 1, NULL); + detach(c); + detachstack(c); + #if SENDMON_KEEPFOCUS_PATCH && !EXRESIZE_PATCH + arrange(c->mon); + #endif // SENDMON_KEEPFOCUS_PATCH + c->mon = m; + #if SCRATCHPADS_PATCH + if (!(c->tags & SPTAGMASK)) + #endif // SCRATCHPADS_PATCH + #if EMPTYVIEW_PATCH + c->tags = (m->tagset[m->seltags] ? m->tagset[m->seltags] : 1); + #else + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + #endif // EMPTYVIEW_PATCH + #if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH + attachx(c); + #else + attach(c); + #endif + attachstack(c); + #if EXRESIZE_PATCH + if (oldm != m) + arrange(oldm); + arrange(m); + focus(c); + restack(m); + #elif SENDMON_KEEPFOCUS_PATCH + arrange(m); + if (hadfocus) { + focus(c); + restack(m); + } else + focus(NULL); + #else + focus(NULL); + arrange(NULL); + #endif // EXRESIZE_PATCH / SENDMON_KEEPFOCUS_PATCH + #if SWITCHTAG_PATCH + if (c->switchtag) + c->switchtag = 0; + #endif // SWITCHTAG_PATCH +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +#if BAR_SYSTRAY_PATCH +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) +#else +sendevent(Client *c, Atom proto) +#endif // BAR_SYSTRAY_PATCH +{ + int n; + Atom *protocols; + #if BAR_SYSTRAY_PATCH + Atom mt; + #endif // BAR_SYSTRAY_PATCH + int exists = 0; + XEvent ev; + + #if BAR_SYSTRAY_PATCH + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; + if (XGetWMProtocols(dpy, w, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + } else { + exists = True; + mt = proto; + } + #else + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + #endif // BAR_SYSTRAY_PATCH + + if (exists) { + #if BAR_SYSTRAY_PATCH + ev.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = mt; + ev.xclient.format = 32; + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &ev); + #else + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + #endif // BAR_SYSTRAY_PATCH + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + #if XKB_PATCH + XkbLockGroup(dpy, XkbUseCoreKbd, c->xkb->group); + #endif // XKB_PATCH + } + #if BAR_SYSTRAY_PATCH + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + #else + sendevent(c, wmatom[WMTakeFocus]); + #endif // BAR_SYSTRAY_PATCH +} + +#if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH +void +setfullscreen(Client *c, int fullscreen) +{ + XEvent ev; + int savestate = 0, restorestate = 0; + + if ((c->fakefullscreen == 0 && fullscreen && !c->isfullscreen) // normal fullscreen + || (c->fakefullscreen == 2 && fullscreen)) // fake fullscreen --> actual fullscreen + savestate = 1; // go actual fullscreen + else if ((c->fakefullscreen == 0 && !fullscreen && c->isfullscreen) // normal fullscreen exit + || (c->fakefullscreen >= 2 && !fullscreen)) // fullscreen exit --> fake fullscreen + restorestate = 1; // go back into tiled + + /* If leaving fullscreen and the window was previously fake fullscreen (2), then restore + * that while staying in fullscreen. The exception to this is if we are in said state, but + * the client itself disables fullscreen (3) then we let the client go out of fullscreen + * while keeping fake fullscreen enabled (as otherwise there will be a mismatch between the + * client and the window manager's perception of the client's fullscreen state). */ + if (c->fakefullscreen == 2 && !fullscreen && c->isfullscreen) { + c->fakefullscreen = 1; + c->isfullscreen = 1; + fullscreen = 1; + } else if (c->fakefullscreen == 3) // client exiting actual fullscreen + c->fakefullscreen = 1; + + if (fullscreen != c->isfullscreen) { // only send property change if necessary + if (fullscreen) + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + else + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + } + + c->isfullscreen = fullscreen; + + /* Some clients, e.g. firefox, will send a client message informing the window manager + * that it is going into fullscreen after receiving the above signal. This has the side + * effect of this function (setfullscreen) sometimes being called twice when toggling + * fullscreen on and off via the window manager as opposed to the application itself. + * To protect against obscure issues where the client settings are stored or restored + * when they are not supposed to we add an additional bit-lock on the old state so that + * settings can only be stored and restored in that precise order. */ + if (savestate && !(c->oldstate & (1 << 1))) { + c->oldbw = c->bw; + c->oldstate = c->isfloating | (1 << 1); + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (restorestate && (c->oldstate & (1 << 1))) { + c->bw = c->oldbw; + c->isfloating = c->oldstate = c->oldstate & 1; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + restack(c->mon); + } else + resizeclient(c, c->x, c->y, c->w, c->h); + + /* Exception: if the client was in actual fullscreen and we exit out to fake fullscreen + * mode, then the focus would sometimes drift to whichever window is under the mouse cursor + * at the time. To avoid this we ask X for all EnterNotify events and just ignore them. + */ + if (!c->isfullscreen) + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} +#else +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + #if !FAKEFULLSCREEN_PATCH + c->oldbw = c->bw; + c->oldstate = c->isfloating; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + #endif // !FAKEFULLSCREEN_PATCH + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + #if !FAKEFULLSCREEN_PATCH + c->bw = c->oldbw; + c->isfloating = c->oldstate; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + #endif // !FAKEFULLSCREEN_PATCH + } +} +#endif // FAKEFULLSCREEN_CLIENT_PATCH + +void +setlayout(const Arg *arg) +{ + #if !TOGGLELAYOUT_PATCH + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) { + #endif // TOGGLELAYOUT_PATCH + #if PERTAG_PATCH + selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + #else + selmon->sellt ^= 1; + #endif // PERTAG_PATCH + #if EXRESIZE_PATCH + if (!selmon->lt[selmon->sellt]->arrange) { + for (Client *c = selmon->clients ; c ; c = c->next) { + if (!c->isfloating) { + /*restore last known float dimensions*/ + resize(c, selmon->mx + c->sfx, selmon->my + c->sfy, + c->sfw, c->sfh, False); + } + } + } + #endif // EXRESIZE_PATCH + #if !TOGGLELAYOUT_PATCH + } + #endif // TOGGLELAYOUT_PATCH + #if TOGGLELAYOUT_PATCH + if (arg && arg->v && arg->v != selmon->lt[selmon->sellt ^ 1]) + #else + if (arg && arg->v) + #endif // TOGGLELAYOUT_PATCH + #if PERTAG_PATCH + selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + #else + selmon->lt[selmon->sellt] = (Layout *)arg->v; + #endif // PERTAG_PATCH + + #if FLEXTILE_DELUXE_LAYOUT + if (selmon->lt[selmon->sellt]->preset.nmaster && selmon->lt[selmon->sellt]->preset.nmaster != -1) + selmon->nmaster = selmon->lt[selmon->sellt]->preset.nmaster; + if (selmon->lt[selmon->sellt]->preset.nstack && selmon->lt[selmon->sellt]->preset.nstack != -1) + selmon->nstack = selmon->lt[selmon->sellt]->preset.nstack; + + selmon->ltaxis[LAYOUT] = selmon->lt[selmon->sellt]->preset.layout; + selmon->ltaxis[MASTER] = selmon->lt[selmon->sellt]->preset.masteraxis; + selmon->ltaxis[STACK] = selmon->lt[selmon->sellt]->preset.stack1axis; + selmon->ltaxis[STACK2] = selmon->lt[selmon->sellt]->preset.stack2axis; + + #if PERTAG_PATCH + selmon->pertag->ltaxis[selmon->pertag->curtag][LAYOUT] = selmon->ltaxis[LAYOUT]; + selmon->pertag->ltaxis[selmon->pertag->curtag][MASTER] = selmon->ltaxis[MASTER]; + selmon->pertag->ltaxis[selmon->pertag->curtag][STACK] = selmon->ltaxis[STACK]; + selmon->pertag->ltaxis[selmon->pertag->curtag][STACK2] = selmon->ltaxis[STACK2]; + #endif // PERTAG_PATCH + #endif // FLEXTILE_DELUXE_LAYOUT + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + #if PERTAG_PATCH + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + #else + selmon->mfact = f; + #endif // PERTAG_PATCH + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + #if XKB_PATCH + XkbStateRec xkbstate; + #endif // XKB_PATCH + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + #if RESTARTSIG_PATCH + signal(SIGHUP, sighup); + signal(SIGTERM, sigterm); + #endif // RESTARTSIG_PATCH + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + #if BAR_ALPHA_PATCH + xinitvisual(); + drw = drw_create(dpy, screen, root, sw, sh, visual, depth, cmap); + #else + drw = drw_create(dpy, screen, root, sw, sh); + #endif // BAR_ALPHA_PATCH + #if BAR_PANGO_PATCH + if (!drw_font_create(drw, font)) + #else + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + #endif // BAR_PANGO_PATCH + die("no fonts could be loaded."); + #if BAR_STATUSPADDING_PATCH + lrpad = drw->fonts->h + horizpadbar; + bh = drw->fonts->h + vertpadbar; + #else + lrpad = drw->fonts->h; + #if BAR_HEIGHT_PATCH + bh = bar_height ? bar_height : drw->fonts->h + 2; + #else + bh = drw->fonts->h + 2; + #endif // BAR_HEIGHT_PATCH + #endif // BAR_STATUSPADDING_PATCH + #if TAB_PATCH + th = bh; + #endif // TAB_PATCH + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + #if WINDOWROLERULE_PATCH + wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False); + #endif // WINDOWROLERULE_PATCH + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + #if BAR_SYSTRAY_PATCH + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + #endif // BAR_SYSTRAY_PATCH + #if BAR_EWMHTAGS_PATCH + netatom[NetDesktopViewport] = XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); + netatom[NetNumberOfDesktops] = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); + netatom[NetCurrentDesktop] = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); + netatom[NetDesktopNames] = XInternAtom(dpy, "_NET_DESKTOP_NAMES", False); + #endif // BAR_EWMHTAGS_PATCH + #if BAR_WINICON_PATCH + netatom[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", False); + #endif // BAR_WINICON_PATCH + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + #if NET_CLIENT_LIST_STACKING_PATCH + netatom[NetClientListStacking] = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); + #endif // NET_CLIENT_LIST_STACKING_PATCH + #if DECORATION_HINTS_PATCH + motifatom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); + #endif // DECORATION_HINTS_PATCH + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + #if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH + cursor[CurResizeBR] = drw_cur_create(drw, XC_bottom_right_corner); + cursor[CurResizeBL] = drw_cur_create(drw, XC_bottom_left_corner); + cursor[CurResizeTR] = drw_cur_create(drw, XC_top_right_corner); + cursor[CurResizeTL] = drw_cur_create(drw, XC_top_left_corner); + #endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH + #if DRAGMFACT_PATCH + cursor[CurResizeHorzArrow] = drw_cur_create(drw, XC_sb_h_double_arrow); + cursor[CurResizeVertArrow] = drw_cur_create(drw, XC_sb_v_double_arrow); + #endif // DRAGMFACT_PATCH + #if DRAGCFACT_PATCH + cursor[CurIronCross] = drw_cur_create(drw, XC_iron_cross); + #endif // DRAGCFACT_PATCH + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + #if BAR_VTCOLORS_PATCH + get_vt_colors(); + if (get_luminance(colors[SchemeTagsNorm][ColBg]) > 50) { + strcpy(colors[SchemeTitleNorm][ColBg], title_bg_light); + strcpy(colors[SchemeTitleSel][ColBg], title_bg_light); + } else { + strcpy(colors[SchemeTitleNorm][ColBg], title_bg_dark); + strcpy(colors[SchemeTitleSel][ColBg], title_bg_dark); + } + #endif // BAR_VTCOLORS_PATCH + #if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH + scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *)); + #if BAR_ALPHA_PATCH + scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], alphas[0], ColCount); + #else + scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], ColCount); + #endif // BAR_ALPHA_PATCH + #else + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + #endif // BAR_STATUS2D_PATCH + for (i = 0; i < LENGTH(colors); i++) + #if BAR_ALPHA_PATCH + scheme[i] = drw_scm_create(drw, colors[i], alphas[i], ColCount); + #else + scheme[i] = drw_scm_create(drw, colors[i], ColCount); + #endif // BAR_ALPHA_PATCH + #if BAR_POWERLINE_STATUS_PATCH + statusscheme = ecalloc(LENGTH(statuscolors), sizeof(Clr *)); + for (i = 0; i < LENGTH(statuscolors); i++) + #if BAR_ALPHA_PATCH + statusscheme[i] = drw_scm_create(drw, statuscolors[i], alphas[0], ColCount); + #else + statusscheme[i] = drw_scm_create(drw, statuscolors[i], ColCount); + #endif // BAR_ALPHA_PATCH + #endif // BAR_POWERLINE_STATUS_PATCH + + updatebars(); + updatestatus(); + + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + #if LG3D_PATCH + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "LG3D", 4); + #else + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + #endif // LG3D_PATCH + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + #if BAR_EWMHTAGS_PATCH + setnumdesktops(); + setcurrentdesktop(); + setdesktopnames(); + setviewport(); + #endif // BAR_EWMHTAGS_PATCH + XDeleteProperty(dpy, root, netatom[NetClientList]); + #if NET_CLIENT_LIST_STACKING_PATCH + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + #endif // NET_CLIENT_LIST_STACKING_PATCH + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + + #if XKB_PATCH + /* get xkb extension info, events and current state */ + if (!XkbQueryExtension(dpy, NULL, &xkbEventType, NULL, NULL, NULL)) + fputs("warning: can not query xkb extension\n", stderr); + XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify, + XkbAllStateComponentsMask, XkbGroupStateMask); + XkbGetState(dpy, XkbUseCoreKbd, &xkbstate); + xkbGlobal.group = xkbstate.locked_group; + #endif // XKB_PATCH + + grabkeys(); + focus(NULL); + #if IPC_PATCH + setupepoll(); + #endif // IPC_PATCH + #if BAR_ANYBAR_PATCH + if (usealtbar) + spawnbar(); + #endif // BAR_ANYBAR_PATCH +} + + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + #if SCRATCHPADS_PATCH && SCRATCHPADS_KEEP_POSITION_AND_SIZE_PATCH + if ( + (c->tags & SPTAGMASK) && + c->isfloating && + ( + c->x < c->mon->mx || + c->x > c->mon->mx + c->mon->mw || + c->y < c->mon->my || + c->y > c->mon->my + c->mon->mh + ) + ) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + #elif SCRATCHPADS_PATCH + if ((c->tags & SPTAGMASK) && c->isfloating) { + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + #endif // SCRATCHPADS_KEEP_POSITION_AND_SIZE_PATCH | SCRATCHPADS_PATCH + /* show clients top down */ + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) { + XMoveWindow(dpy, c->win, c->sfx, c->sfy); + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); + showhide(c->snext); + return; + } + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + #if AUTORESIZE_PATCH + if (c->needresize) { + c->needresize = 0; + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else { + XMoveWindow(dpy, c->win, c->x, c->y); + } + #else + XMoveWindow(dpy, c->win, c->x, c->y); + #endif // AUTORESIZE_PATCH + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) + #if !FAKEFULLSCREEN_PATCH + && !c->isfullscreen + #endif // !FAKEFULLSCREEN_PATCH + ) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +sigchld(int unused) +{ + #if COOL_AUTOSTART_PATCH + pid_t pid; + #endif // COOL_AUTOSTART_PATCH + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + #if COOL_AUTOSTART_PATCH + while (0 < (pid = waitpid(-1, NULL, WNOHANG))) { + pid_t *p, *lim; + + if (!(p = autostart_pids)) + continue; + lim = &p[autostart_len]; + + for (; p < lim; p++) { + if (*p == pid) { + *p = -1; + break; + } + } + } + #else + while (0 < waitpid(-1, NULL, WNOHANG)); + #endif // COOL_AUTOSTART_PATCH +} + +#if RIODRAW_PATCH +void +spawn(const Arg *arg) +{ + spawncmd(arg); +} + +pid_t +spawncmd(const Arg *arg) +#else +void +spawn(const Arg *arg) +#endif // RIODRAW_PATCH +{ + #if RIODRAW_PATCH + pid_t pid; + #endif // RIODRAW_PATCH + #if !NODMENU_PATCH + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + #endif // NODMENU_PATCH + + #if RIODRAW_PATCH + if ((pid = fork()) == 0) + #else + if (fork() == 0) + #endif // RIODRAW_PATCH + { + if (dpy) + close(ConnectionNumber(dpy)); + + #if BAR_STATUSCMD_PATCH && !BAR_DWMBLOCKS_PATCH + if (arg->v == statuscmd) { + for (int i = 0; i < LENGTH(statuscmds); i++) { + if (statuscmdn == statuscmds[i].id) { + statuscmd[2] = statuscmds[i].cmd; + setenv("BUTTON", lastbutton, 1); + break; + } + } + if (!statuscmd[2]) + exit(EXIT_SUCCESS); + } + #endif // BAR_STATUSCMD_PATCH | BAR_DWMBLOCKS_PATCH + #if SPAWNCMD_PATCH + if (selmon->sel) { + const char* const home = getenv("HOME"); + assert(home && strchr(home, '/')); + const size_t homelen = strlen(home); + char *cwd, *pathbuf = NULL; + struct stat statbuf; + + cwd = strtok(selmon->sel->name, SPAWN_CWD_DELIM); + /* NOTE: strtok() alters selmon->sel->name in-place, + * but that does not matter because we are going to + * exec() below anyway; nothing else will use it */ + while (cwd) { + if (*cwd == '~') { /* replace ~ with $HOME */ + if (!(pathbuf = malloc(homelen + strlen(cwd)))) /* ~ counts for NULL term */ + die("fatal: could not malloc() %u bytes\n", homelen + strlen(cwd)); + strcpy(strcpy(pathbuf, home) + homelen, cwd + 1); + cwd = pathbuf; + } + + if (strchr(cwd, '/') && !stat(cwd, &statbuf)) { + if (!S_ISDIR(statbuf.st_mode)) + cwd = dirname(cwd); + + if (!chdir(cwd)) + break; + } + + cwd = strtok(NULL, SPAWN_CWD_DELIM); + } + + free(pathbuf); + } + #endif // SPAWNCMD_PATCH + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); + perror(" failed"); + exit(EXIT_SUCCESS); + } + #if RIODRAW_PATCH + return pid; + #endif // RIODRAW_PATCH +} + +void +tag(const Arg *arg) +{ + #if SWAPFOCUS_PATCH && PERTAG_PATCH + unsigned int tagmask, tagindex; + #endif // SWAPFOCUS_PATCH + + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + #if SWITCHTAG_PATCH + if (selmon->sel->switchtag) + selmon->sel->switchtag = 0; + #endif // SWITCHTAG_PATCH + focus(NULL); + #if SWAPFOCUS_PATCH && PERTAG_PATCH + selmon->pertag->prevclient[selmon->pertag->curtag] = NULL; + for (tagmask = arg->ui & TAGMASK, tagindex = 1; tagmask!=0; tagmask >>= 1, tagindex++) + if (tagmask & 1) + selmon->pertag->prevclient[tagindex] = NULL; + #endif // SWAPFOCUS_PATCH + arrange(selmon); + #if VIEWONTAG_PATCH + if ((arg->ui & TAGMASK) != selmon->tagset[selmon->seltags]) + view(arg); + #endif // VIEWONTAG_PATCH + } +} + +void +tagmon(const Arg *arg) +{ + #if TAGMONFIXFS_PATCH + Client *c = selmon->sel; + if (!c || !mons->next) + return; + if (c->isfullscreen) { + c->isfullscreen = 0; + sendmon(c, dirtomon(arg->i)); + c->isfullscreen = 1; + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + if (c->fakefullscreen != 1) { + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } + #elif !FAKEFULLSCREEN_PATCH + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + #endif // FAKEFULLSCREEN_CLIENT_PATCH + } else + sendmon(c, dirtomon(arg->i)); + #else + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); + #endif // TAGMONFIXFS_PATCH +} + +void +togglebar(const Arg *arg) +{ + Bar *bar; + #if BAR_HOLDBAR_PATCH && PERTAG_PATCH && PERTAGBAR_PATCH + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = (selmon->showbar == 2 ? 1 : !selmon->showbar); + #elif BAR_HOLDBAR_PATCH + selmon->showbar = (selmon->showbar == 2 ? 1 : !selmon->showbar); + #elif PERTAG_PATCH && PERTAGBAR_PATCH + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; + #else + selmon->showbar = !selmon->showbar; + #endif // BAR_HOLDBAR_PATCH | PERTAG_PATCH + updatebarpos(selmon); + for (bar = selmon->bar; bar; bar = bar->next) + XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh); + arrange(selmon); +} + +void +togglefloating(const Arg *arg) +{ + Client *c = selmon->sel; + if (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + #if !FAKEFULLSCREEN_PATCH + #if FAKEFULLSCREEN_CLIENT_PATCH + if (c->isfullscreen && c->fakefullscreen != 1) /* no support for fullscreen windows */ + return; + #else + if (c->isfullscreen) /* no support for fullscreen windows */ + return; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #endif // !FAKEFULLSCREEN_PATCH + c->isfloating = !c->isfloating || c->isfixed; + #if !BAR_FLEXWINTITLE_PATCH + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + #endif // BAR_FLEXWINTITLE_PATCH + if (c->isfloating) { + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + if (c->sfx != -9999) { + /* restore last known float dimensions */ + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); + } else + #endif // SAVEFLOATS_PATCH // EXRESIZE_PATCH + resize(c, c->x, c->y, c->w, c->h, 0); + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + } else { + /* save last known float dimensions */ + c->sfx = c->x; + c->sfy = c->y; + c->sfw = c->w; + c->sfh = c->h; + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + } + arrange(c->mon); + + #if BAR_EWMHTAGS_PATCH + setfloatinghint(c); + #endif // BAR_EWMHTAGS_PATCH +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + #if SWAPFOCUS_PATCH && PERTAG_PATCH + unsigned int tagmask, tagindex; + #endif // SWAPFOCUS_PATCH + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + #if SWAPFOCUS_PATCH && PERTAG_PATCH + for (tagmask = arg->ui & TAGMASK, tagindex = 1; tagmask!=0; tagmask >>= 1, tagindex++) + if (tagmask & 1) + selmon->pertag->prevclient[tagindex] = NULL; + #endif // SWAPFOCUS_PATCH + arrange(selmon); + } + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH +} + +void +toggleview(const Arg *arg) +{ + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + #if PERTAG_PATCH + int i; + #endif // PERTAG_PATCH + + #if TAGINTOSTACK_ALLMASTER_PATCH + Client *const selected = selmon->sel; + + // clients in the master area should be the same after we add a new tag + Client **const masters = calloc(selmon->nmaster, sizeof(Client *)); + if (!masters) { + die("fatal: could not calloc() %u bytes \n", selmon->nmaster * sizeof(Client *)); + } + // collect (from last to first) references to all clients in the master area + Client *c; + size_t j; + for (c = nexttiled(selmon->clients), j = 0; c && j < selmon->nmaster; c = nexttiled(c->next), ++j) + masters[selmon->nmaster - (j + 1)] = c; + // put the master clients at the front of the list + // > go from the 'last' master to the 'first' + for (j = 0; j < selmon->nmaster; ++j) + if (masters[j]) + pop(masters[j]); + free(masters); + + // we also want to be sure not to mutate the focus + focus(selected); + #elif TAGINTOSTACK_ONEMASTER_PATCH + // the first visible client should be the same after we add a new tag + // we also want to be sure not to mutate the focus + Client *const c = nexttiled(selmon->clients); + if (c) { + Client * const selected = selmon->sel; + pop(c); + focus(selected); + } + #endif // TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH + + #if !EMPTYVIEW_PATCH + if (newtagset) { + #endif // EMPTYVIEW_PATCH + selmon->tagset[selmon->seltags] = newtagset; + + #if PERTAG_PATCH + #if SCRATCHPADS_PATCH + if (newtagset == ~SPTAGMASK) + #else + if (newtagset == ~0) + #endif // SCRATCHPADS_PATCH + { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + #if PERTAGBAR_PATCH + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + #endif // PERTAGBAR_PATCH + #endif // PERTAG_PATCH + focus(NULL); + arrange(selmon); + #if !EMPTYVIEW_PATCH + } + #endif // EMPTYVIEW_PATCH + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH +} + +void +unfocus(Client *c, int setfocus, Client *nextfocus) +{ + if (!c) + return; + #if SWAPFOCUS_PATCH && PERTAG_PATCH + selmon->pertag->prevclient[selmon->pertag->curtag] = c; + #endif // SWAPFOCUS_PATCH + #if LOSEFULLSCREEN_PATCH + if (c->isfullscreen && ISVISIBLE(c) && c->mon == selmon && nextfocus && !nextfocus->isfloating) + #if FAKEFULLSCREEN_CLIENT_PATCH + if (c->fakefullscreen != 1) + setfullscreen(c, 0); + #else + setfullscreen(c, 0); + #endif // #if FAKEFULLSCREEN_CLIENT_PATCH + #endif // LOSEFULLSCREEN_PATCH + grabbuttons(c, 0); + #if !BAR_FLEXWINTITLE_PATCH + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + #endif // BAR_FLEXWINTITLE_PATCH + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + #if SWITCHTAG_PATCH + unsigned int switchtag = c->switchtag; + #endif // SWITCHTAG_PATCH + XWindowChanges wc; + #if XKB_PATCH + XkbInfo *xkb; + #endif // XKB_PATCH + + #if SWALLOW_PATCH + if (c->swallowing) { + unswallow(c); + return; + } + + Client *s = swallowingclient(c->win); + if (s) { + free(s->swallowing); + s->swallowing = NULL; + arrange(m); + focus(NULL); + return; + } + #endif // SWALLOW_PATCH + + detach(c); + detachstack(c); + #if BAR_WINICON_PATCH + freeicon(c); + #endif // BAR_WINICON_PATCH + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + #if XKB_PATCH + else { + xkb = findxkb(c->win); + if (xkb != NULL) { + if (xkb->prev) + xkb->prev->next = xkb->next; + if (xkb->next) + xkb->next->prev = xkb->prev; + free(xkb); + } + } + #endif // XKB_PATCH + + #if SCRATCHPAD_ALT_1_PATCH + if (scratchpad_last_showed == c) + scratchpad_last_showed = NULL; + #endif // SCRATCHPAD_ALT_1_PATCH + + free(c); + #if SWALLOW_PATCH + if (s) + return; + #endif // SWALLOW_PATCH + focus(NULL); + updateclientlist(); + arrange(m); + #if SWITCHTAG_PATCH + if (switchtag && ((switchtag & TAGMASK) != selmon->tagset[selmon->seltags])) + view(&((Arg) { .ui = switchtag })); + #endif // SWITCHTAG_PATCH +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + #if BAR_ANYBAR_PATCH + Monitor *m; + Bar *bar; + #endif // BAR_ANYBAR_PATCH + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + #if BAR_SYSTRAY_PATCH + } else if (showsystray && (c = wintosystrayicon(ev->window))) { + /* KLUDGE! sometimes icons occasionally unmap their windows, but do + * _not_ destroy them. We map those windows back */ + XMapRaised(dpy, c->win); + removesystrayicon(c); + drawbarwin(systray->bar); + #endif // BAR_SYSTRAY_PATCH + } + #if BAR_ANYBAR_PATCH + else { + m = wintomon(ev->window); + for (bar = m->bar; bar; bar = bar->next) { + if (bar->win == ev->window) { + unmanagealtbar(ev->window); + break; + } + } + } + #endif // BAR_ANYBAR_PATCH +} + +void +updatebars(void) +{ + Bar *bar; + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + #if BAR_ALPHA_PATCH + .background_pixel = 0, + .border_pixel = 0, + .colormap = cmap, + #else + .background_pixmap = ParentRelative, + #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + for (bar = m->bar; bar; bar = bar->next) { + if (bar->external) + continue; + if (!bar->win) { + #if BAR_ALPHA_PATCH + bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + #else + bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + #endif // BAR_ALPHA_PATCH + XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor); + XMapRaised(dpy, bar->win); + XSetClassHint(dpy, bar->win, &ch); + } + } + #if TAB_PATCH + if (!m->tabwin) { + #if BAR_ALPHA_PATCH + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + #else + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + #endif // BAR_ALPHA_PATCH + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->tabwin); + } + #endif // TAB_PATCH + } +} + +void +updatebarpos(Monitor *m) +{ + #if TAB_PATCH + Client *c; + int nvis = 0; + #endif // TAB_PATCH + + m->wx = m->mx; + m->wy = m->my; + m->ww = m->mw; + m->wh = m->mh; + Bar *bar; + #if BAR_PADDING_PATCH + int y_pad = vertpad; + int x_pad = sidepad; + #else + int y_pad = 0; + int x_pad = 0; + #endif // BAR_PADDING_PATCH + + #if INSETS_PATCH + // Custom insets + Inset inset = m->inset; + m->wx += inset.x; + m->wy += inset.y; + m->ww -= inset.w + inset.x; + m->wh -= inset.h + inset.y; + #endif // INSETS_PATCH + + for (bar = m->bar; bar; bar = bar->next) { + bar->bx = m->wx + x_pad; + #if BAR_ANYBAR_PATCH && !BAR_ANYBAR_MANAGE_WIDTH_PATCH + if (bar->external) + continue; + #endif // BAR_ANYBAR_PATCH | BAR_ANYBAR_MANAGE_WIDTH_PATCH + bar->bw = m->ww - 2 * x_pad; + } + + for (bar = m->bar; bar; bar = bar->next) + if (!m->showbar || !bar->showbar) + bar->by = -bar->bh - y_pad; + + #if TAB_PATCH + for (c = m->clients; c; c = c->next) { + if (ISVISIBLE(c) && !HIDDEN(c)) + ++nvis; + } + + if (m->showtab == showtab_always + || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) { + m->wh -= th; + m->ty = m->toptab ? m->wy : m->wy + m->wh; + if (m->toptab) + m->wy += th; + } else { + m->ty = -th; + } + #endif // TAB_PATCH + + if (!m->showbar) + return; + for (bar = m->bar; bar; bar = bar->next) { + if (!bar->showbar) + continue; + if (bar->topbar) + m->wy = m->wy + bar->bh + y_pad; + m->wh -= y_pad + bar->bh; + bar->by = (bar->topbar ? m->wy - bar->bh : m->wy + m->wh); + } +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + + #if NET_CLIENT_LIST_STACKING_PATCH + XDeleteProperty(dpy, root, netatom[NetClientListStacking]); + for (m = mons; m; m = m->next) + for (c = m->stack; c; c = c->snext) + XChangeProperty(dpy, root, netatom[NetClientListStacking], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + #endif // NET_CLIENT_LIST_STACKING_PATCH +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + #if SORTSCREENS_PATCH + sortscreens(unique, nn); + #endif // SORTSCREENS_PATCH + if (n <= nn) { /* new monitors available */ + for (i = 0; i < (nn - n); i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) { + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + } + } else { /* less monitors available nn < n */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + } + for (i = 0, m = mons; m; m = m->next, i++) + m->index = i; + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + #if SIZEHINTS_PATCH || SIZEHINTS_RULED_PATCH + size.flags = 0; + #else + size.flags = PSize; + #endif // SIZEHINTS_PATCH | SIZEHINTS_RULED_PATCH + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + #if SIZEHINTS_PATCH || SIZEHINTS_RULED_PATCH + if (size.flags & PSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + c->isfloating = 1; + } + #if SIZEHINTS_RULED_PATCH + checkfloatingrules(c); + #endif // SIZEHINTS_RULED_PATCH + #endif // SIZEHINTS_PATCH + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); +} + +void +updatestatus(void) +{ + Monitor *m; + #if BAR_EXTRASTATUS_PATCH + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) { + strcpy(stext, "dwm-"VERSION); + estext[0] = '\0'; + } else { + char *e = strchr(rawstext, statussep); + if (e) { + *e = '\0'; e++; + #if BAR_STATUSCMD_PATCH + strncpy(rawestext, e, sizeof(estext) - 1); + copyvalidchars(estext, rawestext); + #else + strncpy(estext, e, sizeof(estext) - 1); + #endif // BAR_STATUSCMD_PATCH + } else { + estext[0] = '\0'; + } + #if BAR_STATUSCMD_PATCH + copyvalidchars(stext, rawstext); + #else + strncpy(stext, rawstext, sizeof(stext) - 1); + #endif // BAR_STATUSCMD_PATCH + } + #elif BAR_STATUSCMD_PATCH + if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) + strcpy(stext, "dwm-"VERSION); + else + copyvalidchars(stext, rawstext); + #else + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + #endif // BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +updatetitle(Client *c) +{ + #if IPC_PATCH + char oldname[sizeof(c->name)]; + strcpy(oldname, c->name); + #endif // IPC_PATCH + + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); + + #if IPC_PATCH + for (Monitor *m = mons; m; m = m->next) { + if (m->sel == c && strcmp(oldname, c->name) != 0) + ipc_focused_title_change_event(m->num, c->win, oldname, c->name); + } + #endif // IPC_PATCH +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (c->isurgent) { + if (c->isfloating) + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColFloat].pixel); + else + XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColBorder].pixel); + } + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +void +view(const Arg *arg) +{ + #if EMPTYVIEW_PATCH + if (arg->ui && (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + #else + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + #endif // EMPTYVIEW_PATCH + { + #if TOGGLETAG_PATCH + view(&((Arg) { .ui = 0 })); + #endif // TOGGLETAG_PATCH + return; + } + selmon->seltags ^= 1; /* toggle sel tagset */ + #if PERTAG_PATCH + pertagview(arg); + #if SWAPFOCUS_PATCH + Client *unmodified = selmon->pertag->prevclient[selmon->pertag->curtag]; + #endif // SWAPFOCUS_PATCH + #else + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + #endif // PERTAG_PATCH + focus(NULL); + #if SWAPFOCUS_PATCH && PERTAG_PATCH + selmon->pertag->prevclient[selmon->pertag->curtag] = unmodified; + #endif // SWAPFOCUS_PATCH + arrange(selmon); + #if BAR_EWMHTAGS_PATCH + updatecurrentdesktop(); + #endif // BAR_EWMHTAGS_PATCH +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + Bar *bar; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + for (bar = m->bar; bar; bar = bar->next) + if (w == bar->win) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + if (arg && arg->v) + c = (Client*)arg->v; + if (!c) + return; + #if ZOOMSWAP_PATCH + Client *at = NULL, *cold, *cprevious = NULL, *p; + #endif // ZOOMSWAP_PATCH + + #if ZOOMFLOATING_PATCH + if (c && c->isfloating) + togglefloating(&((Arg) { .v = c })); + #endif // ZOOMFLOATING_PATCH + + #if SWAPFOCUS_PATCH && PERTAG_PATCH + c->mon->pertag->prevclient[c->mon->pertag->curtag] = nexttiled(c->mon->clients); + #endif // SWAPFOCUS_PATCH + + if (!c->mon->lt[c->mon->sellt]->arrange + || (c && c->isfloating) + #if ZOOMSWAP_PATCH + || !c + #endif // ZOOMSWAP_PATCH + ) + return; + + #if ZOOMSWAP_PATCH + if (c == nexttiled(c->mon->clients)) { + #if PERTAG_PATCH + p = c->mon->pertag->prevzooms[c->mon->pertag->curtag]; + #else + p = prevzoom; + #endif // PERTAG_PATCH + at = findbefore(p); + if (at) + cprevious = nexttiled(at->next); + if (!cprevious || cprevious != p) { + #if PERTAG_PATCH + c->mon->pertag->prevzooms[c->mon->pertag->curtag] = NULL; + #else + prevzoom = NULL; + #endif // PERTAG_PATCH + #if SWAPFOCUS_PATCH && PERTAG_PATCH + if (!c || !(c = c->mon->pertag->prevclient[c->mon->pertag->curtag] = nexttiled(c->next))) + #else + if (!c || !(c = nexttiled(c->next))) + #endif // SWAPFOCUS_PATCH + return; + } else + #if SWAPFOCUS_PATCH && PERTAG_PATCH + c = c->mon->pertag->prevclient[c->mon->pertag->curtag] = cprevious; + #else + c = cprevious; + #endif // SWAPFOCUS_PATCH + } + + cold = nexttiled(c->mon->clients); + if (c != cold && !at) + at = findbefore(c); + detach(c); + attach(c); + /* swap windows instead of pushing the previous one down */ + if (c != cold && at) { + #if PERTAG_PATCH + c->mon->pertag->prevzooms[c->mon->pertag->curtag] = cold; + #else + prevzoom = cold; + #endif // PERTAG_PATCH + if (cold && at != cold) { + detach(cold); + cold->next = at->next; + at->next = cold; + } + } + focus(c); + arrange(c->mon); + #else + if (c == nexttiled(c->mon->clients)) + #if SWAPFOCUS_PATCH && PERTAG_PATCH + if (!c || !(c = c->mon->pertag->prevclient[c->mon->pertag->curtag] = nexttiled(c->next))) + #else + if (!c || !(c = nexttiled(c->next))) + #endif // SWAPFOCUS_PATCH + return; + pop(c); + #endif // ZOOMSWAP_PATCH +} + +int +main(int argc, char *argv[]) +{ + #if CMDCUSTOMIZE_PATCH + for (int i=1;i<argc;i+=1) + if (!strcmp("-v", argv[i])) + die("dwm-"VERSION); + else if (!strcmp("-h", argv[i]) || !strcmp("--help", argv[i])) + die(help()); + else if (!strcmp("-fn", argv[i])) /* font set */ + #if BAR_PANGO_PATCH + strcpy(font, argv[++i]); + #else + fonts[0] = argv[++i]; + #endif // BAR_PANGO_PATCH + #if !BAR_VTCOLORS_PATCH + else if (!strcmp("-nb", argv[i])) /* normal background color */ + colors[SchemeNorm][1] = argv[++i]; + else if (!strcmp("-nf", argv[i])) /* normal foreground color */ + colors[SchemeNorm][0] = argv[++i]; + else if (!strcmp("-sb", argv[i])) /* selected background color */ + colors[SchemeSel][1] = argv[++i]; + else if (!strcmp("-sf", argv[i])) /* selected foreground color */ + colors[SchemeSel][0] = argv[++i]; + #endif // !BAR_VTCOLORS_PATCH + #if NODMENU_PATCH + else if (!strcmp("-df", argv[i])) /* dmenu font */ + dmenucmd[2] = argv[++i]; + else if (!strcmp("-dnb", argv[i])) /* dmenu normal background color */ + dmenucmd[4] = argv[++i]; + else if (!strcmp("-dnf", argv[i])) /* dmenu normal foreground color */ + dmenucmd[6] = argv[++i]; + else if (!strcmp("-dsb", argv[i])) /* dmenu selected background color */ + dmenucmd[8] = argv[++i]; + else if (!strcmp("-dsf", argv[i])) /* dmenu selected foreground color */ + dmenucmd[10] = argv[++i]; + #else + else if (!strcmp("-df", argv[i])) /* dmenu font */ + dmenucmd[4] = argv[++i]; + else if (!strcmp("-dnb", argv[i])) /* dmenu normal background color */ + dmenucmd[6] = argv[++i]; + else if (!strcmp("-dnf", argv[i])) /* dmenu normal foreground color */ + dmenucmd[8] = argv[++i]; + else if (!strcmp("-dsb", argv[i])) /* dmenu selected background color */ + dmenucmd[10] = argv[++i]; + else if (!strcmp("-dsf", argv[i])) /* dmenu selected foreground color */ + dmenucmd[12] = argv[++i]; + #endif // NODMENU_PATCH + else die(help()); + #else + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + #endif // CMDCUSTOMIZE_PATCH + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + #if SWALLOW_PATCH + if (!(xcon = XGetXCBConnection(dpy))) + die("dwm: cannot get xcb connection\n"); + #endif // SWALLOW_PATCH + checkotherwm(); + #if XRDB_PATCH && !BAR_VTCOLORS_PATCH + XrmInitialize(); + loadxrdb(); + #endif // XRDB_PATCH && !BAR_VTCOLORS_PATCH + #if COOL_AUTOSTART_PATCH + autostart_exec(); + #endif // COOL_AUTOSTART_PATCH + setup(); +#ifdef __OpenBSD__ + #if SWALLOW_PATCH + if (pledge("stdio rpath proc exec ps", NULL) == -1) + #else + if (pledge("stdio rpath proc exec", NULL) == -1) + #endif // SWALLOW_PATCH + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + #if AUTOSTART_PATCH + runautostart(); + #endif + run(); + #if RESTARTSIG_PATCH + if (restart) + execvp(argv[0], argv); + #endif // RESTARTSIG_PATCH + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} + |
