/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XINERAMA #include #endif /* XINERAMA */ #include #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 #endif // BAR_PANGO_PATCH #if XKB_PATCH #include #endif // XKB_PATCH #if SPAWNCMD_PATCH #include #include #include #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 && SAVEFLOATS_PATCH || CENTER_PATCH && EXRESIZE_PATCH c->sfx = -9999; c->sfy = -9999; if (c->iscentered) { c->sfx = c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; c->sfy = c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; } #elif 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; } #elif ALWAYSCENTER_PATCH && SAVEFLOATS_PATCH || ALWAYSCENTER_PATCH && EXRESIZE_PATCH c->sfx = c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; c->sfy = c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; #elif ALWAYSCENTER_PATCH c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; #elif SAVEFLOATS_PATCH || EXRESIZE_PATCH c->sfx = -9999; c->sfy = -9999; #endif // CENTER_PATCH / ALWAYSCENTER_PATCH / SAVEFLOATS_PATCH #if SAVEFLOATS_PATCH || EXRESIZE_PATCH 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) { c->w = wc.width += c->bw * 2; c->h = 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