dwm/dwm.c
bakkeby 36cbcf53a2 IPC: do not bail on events from unknown file descriptors ref. #433
The natural cycle for dwm-msg is that:
   - the client registers
   - the client sends a message
   - a response is sent back
   - the client deregisters

There is a race condition such that a new client may end up with the same
file descriptor as another command that is deregistering, resulting in a
message to come through from a file descriptor that is not registered.

The handling of this situation is that the IPC patch will log:

   Got event from unknown fd 7, ptr 0x7, u32 7, u64 7 with events 17

before gracefully stopping (exiting) dwm.

The consequence of the error itself seems benign and the proposal here is
to allow dwm to keep running despite not being able to process the dwm-msg
command successfully.
2024-08-01 14:56:48 +02:00

5377 lines
143 KiB
C

/* 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 RESTARTSIG_PATCH
#include <poll.h>
#endif // RESTARTSIG_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 NUMVIEWHIST NUMTAGS
#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 && !RENAMED_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 RENAMED_SCRATCHPADS_PATCH
SchemeScratchSel,
SchemeScratchNorm,
#endif // RENAMED_SCRATCHPADS_PATCH
#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 */
#if SEAMLESS_RESTART_PATCH
enum {
ClientFields,
ClientTags,
ClientLast
}; /* dwm client atoms */
#endif // SEAMLESS_RESTART_PATCH
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);
int (*hoverfunc)(Bar *bar, BarArg *a, XMotionEvent *ev);
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
#if SEAMLESS_RESTART_PATCH
unsigned int idx;
#endif // SEAMLESS_RESTART_PATCH
int oldx, oldy, oldw, oldh;
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
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 SIZEHINTS_ISFREESIZE_PATCH
int isfreesize;
#endif // SIZEHINTS_ISFREESIZE_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 RENAMED_SCRATCHPADS_PATCH
char scratchkey;
#endif // RENAMED_SCRATCHPADS_PATCH
#if XKB_PATCH
XkbInfo *xkb;
#endif // XKB_PATCH
#if BAR_WINICON_PATCH
unsigned int icw, ich;
Picture 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 {
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 */
#if PERMON_VANITYGAPS_PATCH
int enablegaps; /* whether gaps are enabled */
#endif // PERMON_VANITYGAPS_PATCH
#endif // VANITYGAPS_PATCH
#if SETBORDERPX_PATCH
int borderpx;
#endif // SETBORDERPX_PATCH
unsigned int seltags;
unsigned int sellt;
#if VIEW_HISTORY_PATCH
unsigned int tagset[NUMVIEWHIST];
#else
unsigned int tagset[2];
#endif // VIEW_HISTORY_PATCH
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;
#if FOCUSMASTER_RETURN_PATCH
Client *tagmarked[32];
#endif // FOCUSMASTER_RETURN_PATCH
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 BAR_TAGLABELS_PATCH
char taglabel[NUMTAGS][64];
#endif // BAR_TAGLABELS_PATCH
#if IPC_PATCH
char lastltsymbol[16];
TagState tagstate;
Client *lastsel;
const Layout *lastlt;
#endif // IPC_PATCH
#if BAR_TAGPREVIEW_PATCH
Window tagwin;
int previewshow;
Pixmap tagmap[NUMTAGS];
#endif // BAR_TAGPREVIEW_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 SIZEHINTS_ISFREESIZE_PATCH
int isfreesize;
#endif // SIZEHINTS_ISFREESIZE_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 RENAMED_SCRATCHPADS_PATCH
const char scratchkey;
#endif // RENAMED_SCRATCHPADS_PATCH
#if UNMANAGED_PATCH
int unmanaged;
#endif // UNMANAGED_PATCH
#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, Atom req);
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);
static void motionnotify(XEvent *e);
static void movemouse(const Arg *arg);
static Client *nexttiled(Client *c);
#if NOBORDER_PATCH
static int noborder(Client *c);
#endif // NOBORDER_PATCH
#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH
static void pop(Client *c);
#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);
#if COOL_AUTOSTART_PATCH
static void sigchld(int unused);
#endif // COOL_AUTOSTART_PATCH
static void showhide(Client *c);
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 */
#if UNMANAGED_PATCH
static int unmanaged = 0; /* whether the window manager should manage the new window or not */
#endif // UNMANAGED_PATCH
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,
[MotionNotify] = motionnotify,
[PropertyNotify] = propertynotify,
#if BAR_SYSTRAY_PATCH
[ResizeRequest] = resizerequest,
#endif // BAR_SYSTRAY_PATCH
[UnmapNotify] = unmapnotify
};
static Atom wmatom[WMLast], netatom[NetLast];
#if BAR_SYSTRAY_PATCH
static Atom xatom[XLast];
#endif // BAR_SYSTRAY_PATCH
#if SEAMLESS_RESTART_PATCH
static Atom clientatom[ClientLast];
#endif // SEAMLESS_RESTART_PATCH
#if ON_EMPTY_KEYS_PATCH
static int isempty = 0;
#endif // ON_EMPTY_KEYS_PATCH
#if RESTARTSIG_PATCH
static volatile sig_atomic_t running = 1;
#else
static int running = 1;
#endif // RESTARTSIG_PATCH
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
#if SIZEHINTS_ISFREESIZE_PATCH
c->isfreesize = 1;
#endif // SIZEHINTS_ISFREESIZE_PATCH
c->isfloating = 0;
c->tags = 0;
#if RENAMED_SCRATCHPADS_PATCH
c->scratchkey = 0;
#endif // RENAMED_SCRATCHPADS_PATCH
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], XA_ATOM);
#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
#if SIZEHINTS_ISFREESIZE_PATCH
c->isfreesize = r->isfreesize;
#endif // SIZEHINTS_ISFREESIZE_PATCH
c->isfloating = r->isfloating;
c->tags |= r->tags;
#if RENAMED_SCRATCHPADS_PATCH
c->scratchkey = r->scratchkey;
#elif 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
#if UNMANAGED_PATCH
unmanaged = r->unmanaged;
#endif // UNMANAGED_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
{
unfocus(selmon->sel, 1, NULL);
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) {
view(&((Arg) { .ui = newtagset }));
} else {
#if TAGSYNC_PATCH
for (m = mons; m; m = m->next)
m->tagset[m->seltags] = newtagset;
arrange(NULL);
#else
c->mon->tagset[c->mon->seltags] = newtagset;
arrange(c->mon);
#endif // TAGSYNC_PATCH
}
}
}
#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 && !RENAMED_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 && !RENAMED_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) {
if (!c->hintsvalid)
updatesizehints(c);
/* 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 BAR_PADDING_SMART_PATCH
updatebarpos(selmon);
for (Bar *bar = selmon->bar; bar; bar = bar->next)
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
#endif // BAR_PADDING_SMART_PATCH
#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);
}
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->num)
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)
{
Monitor *m;
Layout foo = { "", NULL };
size_t i;
#if ALT_TAB_PATCH
alttabend();
#endif // ALT_TAB_PATCH
#if SEAMLESS_RESTART_PATCH
for (m = mons; m; m = m->next)
persistmonitorstate(m);
#endif // SEAMLESS_RESTART_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
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
#if BAR_TAGPREVIEW_PATCH
for (size_t i = 0; i < NUMTAGS; i++)
if (mon->tagmap[i])
XFreePixmap(dpy, mon->tagmap[i]);
XUnmapWindow(dpy, mon->tagwin);
XDestroyWindow(dpy, mon->tagwin);
#endif // BAR_TAGPREVIEW_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;
#if NOBORDER_PATCH
if (noborder(c)) {
ce.width += c->bw * 2;
ce.height += c->bw * 2;
ce.border_width = 0;
}
#endif // NOBORDER_PATCH
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);
#if BAR_TAGPREVIEW_PATCH
createpreview(m);
#endif // BAR_TAGPREVIEW_PATCH
}
arrange(NULL);
focus(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
#if VIEW_HISTORY_PATCH
for (i = 0; i < LENGTH(m->tagset); i++)
m->tagset[i] = 1;
#else
m->tagset[0] = m->tagset[1] = 1;
#endif // VIEW_HISTORY_PATCH
#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->num = mi;
#if MONITOR_RULES_PATCH
for (j = 0; j < LENGTH(monrules); j++) {
mr = &monrules[j];
if ((mr->monitor == -1 || mr->monitor == m->num)
#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 == m->num)
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 = (barborderpx ? barborderpx : 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 = 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 == m->num) && (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 PERMON_VANITYGAPS_PATCH
m->enablegaps = 1;
#endif // PERMON_VANITYGAPS_PATCH
#if SEAMLESS_RESTART_PATCH
restoremonitorstate(m);
#endif // SEAMLESS_RESTART_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;
#if SEAMLESS_RESTART_PATCH
c->idx = 0;
#endif // SEAMLESS_RESTART_PATCH
#if FOCUSMASTER_RETURN_PATCH
for (int i = 1; i < NUMTAGS; i++)
if (c == c->mon->tagmarked[i])
c->mon->tagmarked[i] = NULL;
#endif // FOCUSMASTER_RETURN_PATCH
for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
*tc = c->next;
c->next = NULL;
}
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;
}
c->snext = NULL;
}
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;
#if !BAR_FLEXWINTITLE_PATCH
if (m->showbar)
#endif // BAR_FLEXWINTITLE_PATCH
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->num)
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 FOCUSFOLLOWMOUSE_PATCH
if (!c || !ISVISIBLE(c))
c = getpointerclient();
#endif // FOCUSFOLLOWMOUSE_PATCH
#if STICKY_PATCH
if (!c || !ISVISIBLE(c))
for (c = selmon->stack; c && (!ISVISIBLE(c) || c->issticky); c = c->snext);
#endif // STICKY_PATCH
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 RENAMED_SCRATCHPADS_PATCH
if (c->scratchkey != 0 && c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColFloat].pixel);
else if (c->scratchkey != 0)
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel);
else if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
#else
if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
#endif // RENAMED_SCRATCHPADS_PATCH
#endif // BAR_FLEXWINTITLE_PATCH
setfocus(c);
} else {
#if NODMENU_PATCH
XSetInputFocus(dpy, selmon->bar && selmon->bar->win ? selmon->bar->win : root, RevertToPointerRoot, CurrentTime);
#else
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
#endif // NODMENU_PATCH
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 && lockfullscreen))
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, Atom req)
{
int di;
unsigned long dl;
unsigned char *p = NULL;
Atom da, atom = None;
#if BAR_SYSTRAY_PATCH
if (prop == xatom[XembedInfo])
req = xatom[XembedInfo];
#endif // BAR_SYSTRAY_PATCH
/* FIXME getatomprop should return the number of items and a pointer to
* the stored data instead of this workaround */
if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
&da, &di, &dl, &dl, &p) == Success && p) {
atom = *(Atom *)p;
#if BAR_SYSTRAY_PATCH
if (da == xatom[XembedInfo] && dl == 2)
atom = ((Atom *)p)[1];
#endif // BAR_SYSTRAY_PATCH
XFree(p);
}
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, k;
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
int start, end, skip;
KeySym *syms;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
XDisplayKeycodes(dpy, &start, &end);
syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
if (!syms)
return;
for (k = start; k <= end; k++)
for (i = 0; i < LENGTH(keys); i++)
/* skip modifier codes, we do that ourselves */
if (keys[i].keysym == syms[(k - start) * skip])
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, k,
keys[i].mod | modifiers[j],
root, True,
GrabModeAsync, GrabModeAsync);
#if ON_EMPTY_KEYS_PATCH
if (!selmon->sel)
for (k = start; k <= end; k++)
for (i = 0; i < LENGTH(on_empty_keys); i++)
/* skip modifier codes, we do that ourselves */
if (on_empty_keys[i].keysym == syms[(k - start) * skip])
for (j = 0; j < LENGTH(modifiers); j++)
XGrabKey(dpy, k,
on_empty_keys[i].mod | modifiers[j],
root, True,
GrabModeAsync, GrabModeAsync);
#endif // ON_EMPTY_KEYS_PATCH
XFree(syms);
}
}
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
#if SEAMLESS_RESTART_PATCH
int settings_restored;
#endif // SEAMLESS_RESTART_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 */
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
c->sfx = c->sfy = c->sfw = c->sfh = -9999;
#endif // SAVEFLOATS_PATCH | EXRESIZE_PATCH
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 SEAMLESS_RESTART_PATCH
settings_restored = restoreclientstate(c);
#endif // SEAMLESS_RESTART_PATCH
#if BAR_WINICON_PATCH
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;