dwm/dwm.c

4172 lines
109 KiB
C
Raw Normal View History

2019-09-05 00:16:39 +02:00
/* See LICENSE file for copyright and license details.
*
* dynamic window manager is designed like any other X client as well. It is
* driven through handling X events. In contrast to other X clients, a window
* manager selects for SubstructureRedirectMask on the root window, to receive
* events about window (dis-)appearance. Only one X connection at a time is
* allowed to select for this event mask.
*
* The event handlers of dwm are organized in an array which is accessed
* whenever a new event has been fetched. This allows event dispatching
* in O(1) time.
*
* Each child of the root window is called a client, except windows which have
* set the override_redirect flag. Clients are organized in a linked client
* list on each monitor, the focus history is remembered through a stack list
* on each monitor. Each client contains a bit array to indicate the tags of a
* client.
*
* Keys and tagging rules are organized as arrays and defined in config.h.
*
* To understand everything else, start reading main().
*/
#include <errno.h>
#include <locale.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */
#include <X11/Xft/Xft.h>
2019-09-05 00:32:39 +02:00
#include "patches.h"
2019-09-05 00:16:39 +02:00
#include "drw.h"
#include "util.h"
#if BAR_FLEXWINTITLE_PATCH
#ifndef FLEXTILE_DELUXE_LAYOUT
#define FLEXTILE_DELUXE_LAYOUT 1
#endif
#endif
#if BAR_PANGO_PATCH
#include <pango/pango.h>
#endif // BAR_PANGO_PATCH
2019-10-10 23:50:30 +02:00
#if SPAWNCMD_PATCH
#include <assert.h>
#include <libgen.h>
#include <sys/stat.h>
#define SPAWN_CWD_DELIM " []{}()<>\"':"
#endif // SPAWNCMD_PATCH
2019-09-05 00:16:39 +02:00
/* macros */
#define NUMTAGS 9
#define BARRULES 20
2019-09-05 00:16:39 +02:00
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
#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)))
2019-09-15 00:43:35 +02:00
#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])
2019-09-15 00:43:35 +02:00
#elif STICKY_PATCH
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky)
#else
2019-09-05 00:16:39 +02:00
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
#endif // ATTACHASIDE_PATCH
2019-09-05 00:16:39 +02:00
#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)
[dwm][PATCH] Multiple scratchpads This patch enables multiple scratchpads, each with one assigned window. This enables the same scratchpad workflow that you have in i3. Scratchpads are implemented as special tags, whose mask does not apply to new spawned windows. To assign a window to a scratchpad you have to set up a rule, as you do with regular tags. Windows tagged with scratchpad tags can be set floating or not in the rules array. Most users would probably want them floating (i3 style), but having them tiled does also perfectly work and might fit better the DWM approach. In case they are set floating, the patch moves them to the center of the screen whenever the are shown. The patch can easily be modified to make this last feature configurable in the rules array (see the center patch). The togglescratch function, borrowed from the previous scratchpad patch and slightly modified, can be used to spawn a registered scratchpad process or toggle its view. This function looks for a window tagged on the selected scratchpad. If it is found its view is toggled. If it is not found the corresponding registered command is spawned. The config.def.h shows three examples of its use to spawn a terminal in the first scratchpad tag, a second terminal running ranger on the second scratchpad tag and the keepassxc application to manage passwords on a third scratchpad tag. If you prefer to spawn your scratchpad applications from the startup script, you might opt for binding keys to toggleview instead, as scratchpads are just special tags (you may even extend the TAGKEYS macro to generalize the key bindings).
2020-04-16 16:39:22 +02:00
#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))
2019-09-05 00:16:39 +02:00
/* 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 */
2019-09-13 23:56:05 +02:00
enum {
2020-08-13 15:42:49 +02:00
SchemeNorm,
SchemeSel,
SchemeTitleNorm,
SchemeTitleSel,
SchemeTagsNorm,
SchemeTagsSel,
SchemeHid,
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
2019-09-13 23:56:05 +02:00
}; /* color schemes */
enum {
NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
#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, NetLast
2019-09-12 00:48:29 +02:00
}; /* EWMH atoms */
2020-06-24 16:36:51 +02:00
enum {
WMProtocols,
WMDelete,
WMState,
WMTakeFocus,
#if WINDOWROLERULE_PATCH
WMWindowRole,
#endif // WINDOWROLERULE_PATCH
WMLast
}; /* default atoms */
enum {
#if BAR_STATUSBUTTON_PATCH
2020-06-24 16:36:51 +02:00
ClkButton,
#endif // BAR_STATUSBUTTON_PATCH
2020-06-24 16:36:51 +02:00
ClkTagBar,
ClkLtSymbol,
ClkStatusText,
ClkWinTitle,
ClkClientWin,
ClkRootWin,
ClkLast
}; /* clicks */
2019-09-05 00:16:39 +02:00
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 */
2019-09-05 00:16:39 +02:00
typedef union {
int i;
unsigned int ui;
float f;
const void *v;
} Arg;
2020-07-18 13:03:30 +02:00
typedef struct Monitor Monitor;
typedef struct Bar Bar;
struct Bar {
Window win;
2020-07-18 13:03:30 +02:00
Monitor *mon;
2020-07-18 18:58:39 +02:00
Bar *next;
2020-07-18 13:03:30 +02:00
int idx;
int showbar;
2020-07-18 18:58:39 +02:00
int topbar;
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 max_width;
} BarWidthArg;
typedef struct {
int x;
int w;
} BarDrawArg;
typedef struct {
int rel_x;
int rel_y;
int rel_w;
int rel_h;
} BarClickArg;
typedef struct {
int monitor;
int bar;
int alignment; // see bar alignment enum
2020-07-18 13:03:30 +02:00
int (*widthfunc)(Bar *bar, BarWidthArg *a);
int (*drawfunc)(Bar *bar, BarDrawArg *a);
int (*clickfunc)(Bar *bar, Arg *arg, BarClickArg *a);
char *name; // for debugging
int x, w; // position, width for internal use
} BarRule;
2019-09-05 00:16:39 +02:00
typedef struct {
unsigned int click;
unsigned int mask;
unsigned int button;
void (*func)(const Arg *arg);
const Arg arg;
} Button;
typedef struct Client Client;
struct Client {
char name[256];
float mina, maxa;
#if CFACTS_PATCH
float cfact;
#endif // CFACTS_PATCH
2019-09-05 00:16:39 +02:00
int x, y, w, h;
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
2019-09-05 00:16:39 +02:00
int oldx, oldy, oldw, oldh;
int basew, baseh, incw, inch, maxw, maxh, minw, minh;
int bw, oldbw;
unsigned int tags;
2019-11-26 20:23:58 +01:00
#if SWITCHTAG_PATCH
unsigned int switchtag;
#endif // SWITCHTAG_PATCH
2019-09-05 00:16:39 +02:00
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH
2019-11-21 12:15:16 +01:00
int fakefullscreen;
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-10-24 08:47:00 +02:00
#if EXRESIZE_PATCH
unsigned char expandmask;
int expandx1, expandy1, expandx2, expandy2;
#if !MAXIMIZE_PATCH
int wasfloating;
#endif // MAXIMIZE_PATCH
#endif // EXRESIZE_PATCH
2019-10-04 22:46:47 +02:00
#if MAXIMIZE_PATCH
int ismax, wasfloating;
#endif // MAXIMIZE_PATCH
#if AUTORESIZE_PATCH
int needresize;
#endif // AUTORESIZE_PATCH
2019-09-08 00:23:30 +02:00
#if CENTER_PATCH
int iscentered;
#endif // CENTER_PATCH
2019-10-22 18:30:00 +02:00
#if ISPERMANENT_PATCH
int ispermanent;
#endif // ISPERMANENT_PATCH
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
int isterminal, noswallow;
pid_t pid;
#endif // SWALLOW_PATCH
2020-08-10 17:09:20 +02:00
#if STEAM_PATCH
int issteam;
#endif // STEAM_PATCH
2019-09-15 00:43:35 +02:00
#if STICKY_PATCH
int issticky;
#endif // STICKY_PATCH
2019-09-05 00:16:39 +02:00
Client *next;
Client *snext;
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
Client *swallowing;
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
Monitor *mon;
Window win;
};
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
2019-09-05 00:16:39 +02:00
typedef struct {
const char *symbol;
void (*arrange)(Monitor *);
#if FLEXTILE_DELUXE_LAYOUT
LayoutPreset preset;
#endif // FLEXTILE_DELUXE_LAYOUT
2019-09-05 00:16:39 +02:00
} Layout;
2020-08-10 12:32:58 +02:00
#if INSETS_PATCH
typedef struct {
int x;
int y;
int w;
int h;
} Inset;
#endif // INSETS_PATCH
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
typedef struct Pertag Pertag;
#endif // PERTAG_PATCH
2019-09-05 00:16:39 +02:00
struct Monitor {
int index;
2019-09-05 00:16:39 +02:00
char ltsymbol[16];
float mfact;
#if FLEXTILE_DELUXE_LAYOUT
int ltaxis[4];
int nstack;
#endif // FLEXTILE_DELUXE_LAYOUT
2019-09-05 00:16:39 +02:00
int nmaster;
int num;
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
#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
2019-09-14 23:28:04 +02:00
#if SETBORDERPX_PATCH
unsigned int borderpx;
#endif // SETBORDERPX_PATCH
2019-09-05 00:16:39 +02:00
unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
int showbar;
Client *clients;
Client *sel;
Client *stack;
Monitor *next;
2020-07-18 18:58:39 +02:00
Bar *bar;
2019-09-05 00:16:39 +02:00
const Layout *lt[2];
#if BAR_ALTERNATIVE_TAGS_PATCH
2019-09-09 23:27:10 +02:00
unsigned int alttag;
#endif // BAR_ALTERNATIVE_TAGS_PATCH
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
Pertag *pertag;
#endif // PERTAG_PATCH
2020-08-10 12:32:58 +02:00
#if INSETS_PATCH
Inset inset;
#endif // INSETS_PATCH
2019-09-05 00:16:39 +02:00
};
typedef struct {
const char *class;
2019-09-07 23:46:08 +02:00
#if WINDOWROLERULE_PATCH
const char *role;
#endif // WINDOWROLERULE_PATCH
2019-09-05 00:16:39 +02:00
const char *instance;
const char *title;
const char *wintype;
2019-09-05 00:16:39 +02:00
unsigned int tags;
2019-09-08 00:23:30 +02:00
#if SWITCHTAG_PATCH
int switchtag;
#endif // SWITCHTAG_PATCH
#if CENTER_PATCH
int iscentered;
#endif // CENTER_PATCH
2019-09-05 00:16:39 +02:00
int isfloating;
2019-10-22 18:30:00 +02:00
#if ISPERMANENT_PATCH
int ispermanent;
#endif // ISPERMANENT_PATCH
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
int isterminal;
int noswallow;
#endif // SWALLOW_PATCH
2020-06-21 15:33:29 +02:00
#if FLOATPOS_PATCH
const char *floatpos;
#endif // FLOATPOS_PATCH
2019-09-05 00:16:39 +02:00
int monitor;
} Rule;
#define RULE(...) { .monitor = -1, ##__VA_ARGS__ },
/* 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 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
2019-09-11 08:28:00 +02:00
#if MONITOR_RULES_PATCH
typedef struct {
int monitor;
#if PERTAG_PATCH
int tag;
#endif // PERTAG_PATCH
2019-09-11 08:28:00 +02:00
int layout;
float mfact;
int nmaster;
int showbar;
int topbar;
2019-09-11 08:28:00 +02:00
} MonitorRule;
#endif // MONITOR_RULES_PATCH
2019-09-05 00:16:39 +02:00
/* 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);
2020-07-18 13:03:30 +02:00
static void drawbarwin(Bar *bar);
2019-09-16 00:32:28 +02:00
#if !FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
static void enternotify(XEvent *e);
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
static void expose(XEvent *e);
static void focus(Client *c);
static void focusin(XEvent *e);
static void focusmon(const Arg *arg);
#if !STACKER_PATCH
2019-09-05 00:16:39 +02:00
static void focusstack(const Arg *arg);
#endif // STACKER_PATCH
static Atom getatomprop(Client *c, Atom prop);
2019-09-05 00:16:39 +02:00
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);
2020-05-31 15:14:29 +02:00
#if KEYMODES_PATCH
static void grabdefkeys(void);
#else
2019-09-05 00:16:39 +02:00
static void grabkeys(void);
2020-05-31 15:14:29 +02:00
#endif // KEYMODES_PATCH
2019-09-05 00:16:39 +02:00
static void incnmaster(const Arg *arg);
2020-05-31 15:14:29 +02:00
#if KEYMODES_PATCH
static void keydefpress(XEvent *e);
#else
2019-09-05 00:16:39 +02:00
static void keypress(XEvent *e);
2020-05-31 15:14:29 +02:00
#endif // KEYMODES_PATCH
2019-09-05 00:16:39 +02:00
static void killclient(const Arg *arg);
static void manage(Window w, XWindowAttributes *wa);
static void mappingnotify(XEvent *e);
static void maprequest(XEvent *e);
2019-09-16 00:32:28 +02:00
#if !FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
static void motionnotify(XEvent *e);
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
static void movemouse(const Arg *arg);
static Client *nexttiled(Client *c);
#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH
2019-09-05 00:16:39 +02:00
static void pop(Client *);
#endif // !ZOOMSWAP_PATCH / TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH
2019-09-05 00:16:39 +02:00
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
2019-09-05 22:10:00 +02:00
static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
#else
2019-09-05 00:16:39 +02:00
static int sendevent(Client *c, Atom proto);
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
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);
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);
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 */
2019-09-05 22:10:00 +02:00
#include "patch/include.h"
2019-09-05 00:16:39 +02:00
/* variables */
static const char broken[] = "broken";
#if BAR_PANGO_PATCH || BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH
2020-05-26 20:53:53 +02:00
static char stext[1024];
#else
static char stext[512];
#endif // BAR_PANGO_PATCH | BAR_STATUS2D_PATCH
2020-07-18 21:12:30 +02:00
#if BAR_EXTRASTATUS_PATCH || BAR_STATUSCMD_PATCH
#if BAR_STATUS2D_PATCH
2020-07-05 21:53:36 +02:00
static char rawstext[1024];
#else
static char rawstext[512];
#endif // BAR_STATUS2D_PATCH
2020-07-18 21:12:30 +02:00
#endif // BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH
#if BAR_EXTRASTATUS_PATCH
#if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH
2020-05-26 20:53:53 +02:00
static char estext[1024];
#else
static char estext[512];
#endif // BAR_STATUS2D_PATCH
#if BAR_STATUSCMD_PATCH
2020-07-05 21:53:36 +02:00
static char rawestext[1024];
2020-07-19 16:57:30 +02:00
#endif // BAR_STATUS2D_PATCH | BAR_STATUSCMD_PATCH
2020-07-18 21:12:30 +02:00
#endif // BAR_EXTRASTATUS_PATCH
2020-07-05 21:53:36 +02:00
2019-09-05 00:16:39 +02:00
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar geometry */
2019-09-05 00:16:39 +02:00
static int lrpad; /* sum of left and right padding for text */
static int (*xerrorxlib)(Display *, XErrorEvent *);
static unsigned int numlockmask = 0;
static void (*handler[LASTEvent]) (XEvent *) = {
[ButtonPress] = buttonpress,
#if COMBO_PATCH || BAR_HOLDBAR_PATCH
2019-09-11 23:35:43 +02:00
[ButtonRelease] = keyrelease,
#endif // COMBO_PATCH / BAR_HOLDBAR_PATCH
2019-09-05 00:16:39 +02:00
[ClientMessage] = clientmessage,
[ConfigureRequest] = configurerequest,
[ConfigureNotify] = configurenotify,
[DestroyNotify] = destroynotify,
2019-09-16 00:32:28 +02:00
#if !FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
[EnterNotify] = enternotify,
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
[Expose] = expose,
[FocusIn] = focusin,
[KeyPress] = keypress,
#if COMBO_PATCH || BAR_HOLDBAR_PATCH
2019-09-11 23:35:43 +02:00
[KeyRelease] = keyrelease,
#endif // COMBO_PATCH / BAR_HOLDBAR_PATCH
2019-09-05 00:16:39 +02:00
[MappingNotify] = mappingnotify,
[MapRequest] = maprequest,
2019-09-16 00:32:28 +02:00
#if !FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
[MotionNotify] = motionnotify,
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
[PropertyNotify] = propertynotify,
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
[ResizeRequest] = resizerequest,
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
[UnmapNotify] = unmapnotify
};
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
#else
2019-09-05 00:16:39 +02:00
static Atom wmatom[WMLast], netatom[NetLast];
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
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"
2019-09-05 00:32:39 +02:00
#include "patch/include.c"
2019-09-05 00:16:39 +02:00
/* 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
2019-09-05 00:16:39 +02:00
/* function implementations */
void
applyrules(Client *c)
{
const char *class, *instance;
Atom wintype;
2019-09-07 23:46:08 +02:00
#if WINDOWROLERULE_PATCH
char role[64];
#endif // WINDOWROLERULE_PATCH
2019-09-05 00:16:39 +02:00
unsigned int i;
#if SWITCHTAG_PATCH
unsigned int newtagset;
#endif // SWITCHTAG_PATCH
2019-09-05 00:16:39 +02:00
const Rule *r;
Monitor *m;
XClassHint ch = { NULL, NULL };
/* rule matching */
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]);
2019-09-07 23:46:08 +02:00
#if WINDOWROLERULE_PATCH
gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role));
#endif // WINDOWROLERULE_PATCH
2019-09-05 00:16:39 +02:00
2020-08-10 17:09:20 +02:00
#if STEAM_PATCH
if (strstr(class, "Steam") || strstr(class, "steam_app_"))
c->issteam = 1;
#endif // STEAM_PATCH
2019-09-05 00:16:39 +02:00
for (i = 0; i < LENGTH(rules); i++) {
r = &rules[i];
if ((!r->title || strstr(c->name, r->title))
&& (!r->class || strstr(class, r->class))
2019-09-07 23:46:08 +02:00
#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)))
2019-09-05 00:16:39 +02:00
{
2019-09-08 00:23:30 +02:00
#if CENTER_PATCH
c->iscentered = r->iscentered;
#endif // CENTER_PATCH
2019-10-22 18:30:00 +02:00
#if ISPERMANENT_PATCH
c->ispermanent = r->ispermanent;
#endif // ISPERMANENT_PATCH
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
c->isterminal = r->isterminal;
c->noswallow = r->noswallow;
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
c->isfloating = r->isfloating;
c->tags |= r->tags;
#if SCRATCHPADS_PATCH && !SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
[dwm][PATCH] Multiple scratchpads This patch enables multiple scratchpads, each with one assigned window. This enables the same scratchpad workflow that you have in i3. Scratchpads are implemented as special tags, whose mask does not apply to new spawned windows. To assign a window to a scratchpad you have to set up a rule, as you do with regular tags. Windows tagged with scratchpad tags can be set floating or not in the rules array. Most users would probably want them floating (i3 style), but having them tiled does also perfectly work and might fit better the DWM approach. In case they are set floating, the patch moves them to the center of the screen whenever the are shown. The patch can easily be modified to make this last feature configurable in the rules array (see the center patch). The togglescratch function, borrowed from the previous scratchpad patch and slightly modified, can be used to spawn a registered scratchpad process or toggle its view. This function looks for a window tagged on the selected scratchpad. If it is found its view is toggled. If it is not found the corresponding registered command is spawned. The config.def.h shows three examples of its use to spawn a terminal in the first scratchpad tag, a second terminal running ranger on the second scratchpad tag and the keepassxc application to manage passwords on a third scratchpad tag. If you prefer to spawn your scratchpad applications from the startup script, you might opt for binding keys to toggleview instead, as scratchpads are just special tags (you may even extend the TAGKEYS macro to generalize the key bindings).
2020-04-16 16:39:22 +02:00
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 | SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
2020-06-23 11:55:55 +02:00
for (m = mons; m && m->num != r->monitor; m = m->next);
if (m)
c->mon = m;
2020-06-21 15:33:29 +02:00
#if FLOATPOS_PATCH
if (c->isfloating && r->floatpos)
setfloatpos(c, r->floatpos);
#endif // FLOATPOS_PATCH
2019-09-08 00:23:30 +02:00
#if SWITCHTAG_PATCH
#if SWALLOW_PATCH
if (r->switchtag && (c->noswallow || !termforwin(c)))
#else
if (r->switchtag)
#endif // SWALLOW_PATCH
{
selmon = c->mon;
if (r->switchtag == 2 || r->switchtag == 4)
2019-09-08 00:23:30 +02:00
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) {
2019-11-26 20:23:58 +01:00
#if PERTAG_PATCH
pertagview(&((Arg) { .ui = newtagset }));
arrange(c->mon);
2019-11-26 20:23:58 +01:00
#else
view(&((Arg) { .ui = newtagset }));
#endif // PERTAG_PATCH
} else {
c->mon->tagset[c->mon->seltags] = newtagset;
arrange(c->mon);
}
2019-09-08 00:23:30 +02:00
}
}
#endif // SWITCHTAG_PATCH
2019-09-05 00:16:39 +02:00
}
}
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];
[dwm][PATCH] Multiple scratchpads This patch enables multiple scratchpads, each with one assigned window. This enables the same scratchpad workflow that you have in i3. Scratchpads are implemented as special tags, whose mask does not apply to new spawned windows. To assign a window to a scratchpad you have to set up a rule, as you do with regular tags. Windows tagged with scratchpad tags can be set floating or not in the rules array. Most users would probably want them floating (i3 style), but having them tiled does also perfectly work and might fit better the DWM approach. In case they are set floating, the patch moves them to the center of the screen whenever the are shown. The patch can easily be modified to make this last feature configurable in the rules array (see the center patch). The togglescratch function, borrowed from the previous scratchpad patch and slightly modified, can be used to spawn a registered scratchpad process or toggle its view. This function looks for a window tagged on the selected scratchpad. If it is found its view is toggled. If it is not found the corresponding registered command is spawned. The config.def.h shows three examples of its use to spawn a terminal in the first scratchpad tag, a second terminal running ranger on the second scratchpad tag and the keepassxc application to manage passwords on a third scratchpad tag. If you prefer to spawn your scratchpad applications from the startup script, you might opt for binding keys to toggleview instead, as scratchpads are just special tags (you may even extend the TAGKEYS macro to generalize the key bindings).
2020-04-16 16:39:22 +02:00
#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
[dwm][PATCH] Multiple scratchpads This patch enables multiple scratchpads, each with one assigned window. This enables the same scratchpad workflow that you have in i3. Scratchpads are implemented as special tags, whose mask does not apply to new spawned windows. To assign a window to a scratchpad you have to set up a rule, as you do with regular tags. Windows tagged with scratchpad tags can be set floating or not in the rules array. Most users would probably want them floating (i3 style), but having them tiled does also perfectly work and might fit better the DWM approach. In case they are set floating, the patch moves them to the center of the screen whenever the are shown. The patch can easily be modified to make this last feature configurable in the rules array (see the center patch). The togglescratch function, borrowed from the previous scratchpad patch and slightly modified, can be used to spawn a registered scratchpad process or toggle its view. This function looks for a window tagged on the selected scratchpad. If it is found its view is toggled. If it is not found the corresponding registered command is spawned. The config.def.h shows three examples of its use to spawn a terminal in the first scratchpad tag, a second terminal running ranger on the second scratchpad tag and the keepassxc application to manage passwords on a third scratchpad tag. If you prefer to spawn your scratchpad applications from the startup script, you might opt for binding keys to toggleview instead, as scratchpads are just special tags (you may even extend the TAGKEYS macro to generalize the key bindings).
2020-04-16 16:39:22 +02:00
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
2019-09-05 00:16:39 +02:00
c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
#endif // EMPTYVIEW_PATCH
2019-09-05 00:16:39 +02:00
}
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)
{
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
if (m->lt[m->sellt]->arrange)
m->lt[m->sellt]->arrange(m);
2020-03-31 08:21:00 +02:00
#if ROUNDED_CORNERS_PATCH
Client *c;
for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
drawroundedcorners(c);
#endif // ROUNDED_CORNERS_PATCH
2019-09-05 00:16:39 +02:00
}
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;
2019-09-05 00:16:39 +02:00
Arg arg = {0};
Client *c;
Monitor *m;
2020-07-18 18:58:39 +02:00
Bar *bar;
2019-09-05 00:16:39 +02:00
XButtonPressedEvent *ev = &e->xbutton;
const BarRule *br;
BarClickArg carg = { 0, 0, 0, 0 };
2019-09-05 00:16:39 +02:00
click = ClkRootWin;
/* focus monitor if necessary */
2019-09-16 00:32:28 +02:00
if ((m = wintomon(ev->window)) && m != selmon
#if FOCUSONCLICK_PATCH
&& (focusonwheel || (ev->button != Button4 && ev->button != Button5))
#endif // FOCUSONCLICK_PATCH
) {
2019-09-05 00:16:39 +02:00
unfocus(selmon->sel, 1);
selmon = m;
focus(NULL);
}
2019-09-10 00:18:46 +02:00
2020-07-18 18:58:39 +02:00
for (bar = selmon->bar; bar; bar = bar->next) {
if (ev->window == bar->win) {
for (r = 0; r < LENGTH(barrules); r++) {
br = &barrules[r];
2020-07-18 18:58:39 +02:00
if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL)
2020-07-18 13:03:30 +02:00
continue;
if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->index)
continue;
2020-07-18 18:58:39 +02:00
if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) {
carg.rel_x = ev->x - bar->x[r];
carg.rel_y = ev->y;
2020-07-18 18:58:39 +02:00
carg.rel_w = bar->w[r];
carg.rel_h = bar->bh;
click = br->clickfunc(bar, &arg, &carg);
if (click < 0)
return;
break;
2020-07-05 21:53:36 +02:00
}
}
break;
2020-07-05 21:53:36 +02:00
}
}
if (click == ClkRootWin && (c = wintoclient(ev->window))) {
2019-09-16 00:32:28 +02:00
#if FOCUSONCLICK_PATCH
if (focusonwheel || (ev->button != Button4 && ev->button != Button5))
focus(c);
#else
2019-09-05 00:16:39 +02:00
focus(c);
restack(selmon);
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
XAllowEvents(dpy, ReplayPointer, CurrentTime);
click = ClkClientWin;
}
for (i = 0; i < LENGTH(buttons); i++) {
2019-09-05 00:16:39 +02:00
if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) {
2020-08-13 15:42:49 +02:00
#if BAR_WINTITLEACTIONS_PATCH
2019-09-10 00:18:46 +02:00
buttons[i].func((click == ClkTagBar || click == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
#else
2019-09-05 00:16:39 +02:00
buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
2020-08-13 15:42:49 +02:00
#endif // BAR_WINTITLEACTIONS_PATCH
}
}
2019-09-05 00:16:39 +02:00
}
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) {
if (systray->win) {
XUnmapWindow(dpy, systray->win);
XDestroyWindow(dpy, systray->win);
}
2019-09-05 22:10:00 +02:00
free(systray);
}
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]);
#if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH
2020-05-26 20:53:53 +02:00
for (i = 0; i < LENGTH(colors) + 1; i++)
#else
2019-09-05 00:16:39 +02:00
for (i = 0; i < LENGTH(colors); i++)
#endif // BAR_STATUS2D_PATCH
2019-09-05 00:16:39 +02:00
free(scheme[i]);
free(scheme);
2019-09-05 00:16:39 +02:00
XDestroyWindow(dpy, wmcheckwin);
drw_free(drw);
XSync(dpy, False);
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
}
void
cleanupmon(Monitor *mon)
{
Monitor *m;
2020-07-18 18:58:39 +02:00
Bar *bar;
2019-09-05 00:16:39 +02:00
if (mon == mons)
mons = mons->next;
else {
for (m = mons; m && m->next != mon; m = m->next);
m->next = mon->next;
}
2020-07-18 18:58:39 +02:00
for (bar = mon->bar; bar; bar = mon->bar) {
XUnmapWindow(dpy, bar->win);
XDestroyWindow(dpy, bar->win);
mon->bar = bar->next;
free(bar);
}
2019-09-05 00:16:39 +02:00
free(mon);
}
void
clientmessage(XEvent *e)
{
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
XWindowAttributes wa;
XSetWindowAttributes swa;
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window);
#if FOCUSONNETACTIVE_PATCH
unsigned int i;
#endif // FOCUSONNETACTIVE_PATCH
2019-09-05 00:16:39 +02:00
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
if (showsystray && 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);
2019-09-05 22:10:00 +02:00
XReparentWindow(dpy, c->win, systray->win, 0, 0);
/* use parents background color */
2020-07-21 11:35:26 +02:00
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
2019-09-05 22:10:00 +02:00
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
2019-09-05 22:10:00 +02:00
2019-09-05 00:16:39 +02:00
if (!c)
return;
if (cme->message_type == netatom[NetWMState]) {
if (cme->data.l[1] == netatom[NetWMFullscreen]
|| cme->data.l[2] == netatom[NetWMFullscreen]) {
#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH
if (c->fakefullscreen)
resizeclient(c, c->x, c->y, c->w, c->h);
else
setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */
&& !c->isfullscreen
)));
#else
2019-09-05 00:16:39 +02:00
setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
2019-10-08 23:15:11 +02:00
|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */
#if !FAKEFULLSCREEN_PATCH
&& !c->isfullscreen
#endif // !FAKEFULLSCREEN_PATCH
)));
#endif // FAKEFULLSCREEN_CLIENT_PATCH
}
2019-09-05 00:16:39 +02:00
} else if (cme->message_type == netatom[NetActiveWindow]) {
#if FOCUSONNETACTIVE_PATCH
if (c->tags & c->mon->tagset[c->mon->seltags]) {
selmon = c->mon;
focus(c);
} else {
for (i = 0; i < NUMTAGS && !((1 << i) & c->tags); i++);
if (i < NUMTAGS) {
const Arg a = {.ui = 1 << i};
selmon = c->mon;
view(&a);
focus(c);
restack(selmon);
}
}
#else
2019-09-05 00:16:39 +02:00
if (c != selmon->sel && !c->isurgent)
seturgent(c, 1);
#endif // FOCUSONNETACTIVE_PATCH
2019-09-05 00:16:39 +02:00
}
}
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;
2020-07-18 18:58:39 +02:00
Bar *bar;
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
Client *c;
2019-10-08 23:15:11 +02:00
#endif // !FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
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, bh);
updatebars();
for (m = mons; m; m = m->next) {
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
for (c = m->clients; c; c = c->next)
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
if (c->isfullscreen && !c->fakefullscreen)
#else
2019-09-05 00:16:39 +02:00
if (c->isfullscreen)
2019-11-21 12:15:16 +01:00
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-09-05 00:16:39 +02:00
resizeclient(c, m->mx, m->my, m->mw, m->mh);
2019-10-08 23:15:11 +02:00
#endif // !FAKEFULLSCREEN_PATCH
2020-07-18 18:58:39 +02:00
for (bar = m->bar; bar; bar = bar->next)
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
2019-09-05 00:16:39 +02:00
}
focus(NULL);
arrange(NULL);
}
}
}
void
configurerequest(XEvent *e)
{
Client *c;
Monitor *m;
XConfigureRequestEvent *ev = &e->xconfigurerequest;
XWindowChanges wc;
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;
2020-08-10 17:09:20 +02:00
#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
2019-09-05 00:16:39 +02:00
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;
}
2020-08-10 17:09:20 +02:00
#endif // STEAM_PATCH
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
} else
configure(c);
} else {
wc.x = ev->x;
wc.y = ev->y;
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
2020-07-18 18:58:39 +02:00
const BarRule *br;
Bar *bar;
2019-09-11 08:28:00 +02:00
#if MONITOR_RULES_PATCH
2020-07-18 18:58:39 +02:00
int j;
2019-09-11 08:28:00 +02:00
const MonitorRule *mr;
#endif // MONITOR_RULES_PATCH
2019-09-05 00:16:39 +02:00
m = ecalloc(1, sizeof(Monitor));
#if EMPTYVIEW_PATCH
m->tagset[0] = m->tagset[1] = 0;
#else
2019-09-05 00:16:39 +02:00
m->tagset[0] = m->tagset[1] = 1;
#endif // EMPTYVIEW_PATCH
2019-09-05 00:16:39 +02:00
m->mfact = mfact;
m->nmaster = nmaster;
#if FLEXTILE_DELUXE_LAYOUT
m->nstack = nstack;
#endif // FLEXTILE_DELUXE_LAYOUT
2019-09-05 00:16:39 +02:00
m->showbar = showbar;
2019-09-14 23:28:04 +02:00
#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
2020-07-18 13:03:30 +02:00
for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index
m->index = mi;
2019-09-11 08:28:00 +02:00
#if MONITOR_RULES_PATCH
for (j = 0; j < LENGTH(monrules); j++) {
mr = &monrules[j];
2020-07-18 13:03:30 +02:00
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];
2019-09-11 08:28:00 +02:00
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)
2020-07-18 18:58:39 +02:00
istopbar = mr->topbar;
2019-09-11 08:28:00 +02:00
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
2019-09-09 22:25:19 +02:00
2020-07-18 18:58:39 +02:00
/* 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);
}
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;
2020-07-18 18:58:39 +02:00
}
#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
2019-09-05 22:58:16 +02:00
#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;
2019-09-05 22:58:16 +02:00
/* 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
2019-09-05 22:58:16 +02:00
/* init layouts */
#if MONITOR_RULES_PATCH
for (j = 0; j < LENGTH(monrules); j++) {
mr = &monrules[j];
2020-07-18 13:03:30 +02:00
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
2019-09-05 22:58:16 +02:00
m->pertag->ltidxs[i][0] = m->lt[0];
m->pertag->ltidxs[i][1] = m->lt[1];
#if FLEXTILE_DELUXE_LAYOUT
2019-09-09 22:25:19 +02:00
/* 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;
2019-09-09 22:25:19 +02:00
#if VANITYGAPS_PATCH
m->pertag->enablegaps[i] = 1;
#endif // VANITYGAPS_PATCH
2019-09-05 22:58:16 +02:00
}
#endif // PERTAG_PATCH
2020-08-10 12:32:58 +02:00
#if INSETS_PATCH
m->inset = default_inset;
#endif // INSETS_PATCH
2019-09-05 00:16:39 +02:00
return m;
}
void
destroynotify(XEvent *e)
{
Client *c;
XDestroyWindowEvent *ev = &e->xdestroywindow;
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
else if ((c = swallowingclient(ev->window)))
unmanage(c->swallowing, 1);
#endif // SWALLOW_PATCH
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
else if (showsystray && (c = wintosystrayicon(ev->window))) {
removesystrayicon(c);
2020-07-18 13:03:30 +02:00
drawbarwin(systray->bar);
2019-09-05 22:10:00 +02:00
}
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
}
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)
{
2020-07-18 18:58:39 +02:00
Bar *bar;
for (bar = m->bar; bar; bar = bar->next)
drawbarwin(bar);
2020-07-18 13:03:30 +02:00
}
2019-09-05 00:16:39 +02:00
2020-07-18 13:03:30 +02:00
void
drawbars(void)
{
Monitor *m;
for (m = mons; m; m = m->next)
drawbar(m);
}
2019-09-05 22:10:00 +02:00
2020-07-18 13:03:30 +02:00
void
drawbarwin(Bar *bar)
{
if (!bar->win)
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;
BarWidthArg warg = { 0 };
BarDrawArg darg = { 0, 0 };
2020-07-18 13:03:30 +02:00
rw = lw = bar->bw;
rx = lx = 0;
2019-09-05 22:10:00 +02:00
2020-06-24 16:36:51 +02:00
drw_setscheme(drw, scheme[SchemeNorm]);
2020-07-18 13:03:30 +02:00
drw_rect(drw, lx, 0, lw, bh, 1, 1);
for (r = 0; r < LENGTH(barrules); r++) {
br = &barrules[r];
if (br->bar != bar->idx || br->drawfunc == NULL || (br->monitor == 'A' && bar->mon != selmon))
continue;
if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->index)
2020-07-18 13:03:30 +02:00
continue;
2019-09-05 00:16:39 +02:00
drw_setscheme(drw, scheme[SchemeNorm]);
2020-07-18 13:03:30 +02:00
warg.max_width = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw);
w = br->widthfunc(bar, &warg);
w = MIN(warg.max_width, w);
2020-07-18 18:58:39 +02:00
if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa
2020-07-18 13:03:30 +02:00
lw = rw;
lx = rx;
} else if (rw <= 0) {
rw = lw;
rx = lx;
}
2019-09-13 23:56:05 +02:00
2020-07-18 13:03:30 +02:00
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;
2019-09-10 00:18:46 +02:00
}
2020-07-18 13:03:30 +02:00
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;
2020-07-18 13:03:30 +02:00
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;
2019-09-06 23:47:06 +02:00
}
2020-07-18 13:03:30 +02:00
lw = bar->x[r] - lx;
break;
case BAR_ALIGN_RIGHT_LEFT:
bar->x[r] = rx;
if (lx == rx) {
lx += w;
lw -= w;
2019-09-06 23:47:06 +02:00
}
2020-07-18 13:03:30 +02:00
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;
}
2020-07-18 13:03:30 +02:00
bar->w[r] = w;
darg.x = bar->x[r];
darg.w = bar->w[r];
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);
2019-10-24 07:35:00 +02:00
}
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);
2019-09-05 00:16:39 +02:00
}
2019-09-16 00:32:28 +02:00
#if !FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
void
enternotify(XEvent *e)
{
Client *c;
#if LOSEFULLSCREEN_PATCH
Client *sel;
#endif // LOSEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
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);
#else
2019-09-05 00:16:39 +02:00
unfocus(selmon->sel, 1);
selmon = m;
#endif // LOSEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
} else if (!c || c == selmon->sel)
return;
focus(c);
}
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
void
expose(XEvent *e)
{
Monitor *m;
XExposeEvent *ev = &e->xexpose;
if (ev->count == 0 && (m = wintomon(ev->window)))
2019-09-05 00:16:39 +02:00
drawbar(m);
}
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);
if (c) {
if (c->mon != selmon)
selmon = c->mon;
if (c->isurgent)
seturgent(c, 0);
detachstack(c);
attachstack(c);
grabbuttons(c, 1);
2019-09-11 00:51:37 +02:00
if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
2019-09-05 00:16:39 +02:00
setfocus(c);
} else {
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
}
selmon->sel = c;
drawbars();
}
/* 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
2019-09-05 00:16:39 +02:00
if (!mons->next)
return;
if ((m = dirtomon(arg->i)) == selmon)
return;
#if LOSEFULLSCREEN_PATCH
sel = selmon->sel;
selmon = m;
unfocus(sel, 0);
#else
2019-09-05 00:16:39 +02:00
unfocus(selmon->sel, 0);
selmon = m;
#endif // LOSEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
focus(NULL);
2019-09-15 00:43:35 +02:00
#if WARP_PATCH
warp(selmon->sel);
#endif // WARP_PATCH
2019-09-05 00:16:39 +02:00
}
#if !STACKER_PATCH
2019-09-05 00:16:39 +02:00
void
focusstack(const Arg *arg)
{
Client *c = NULL, *i;
if (!selmon->sel)
return;
#if ALWAYSFULLSCREEN_PATCH
if (selmon->sel->isfullscreen)
return;
#endif // ALWAYSFULLSCREEN_PATCH
2020-08-13 15:42:49 +02:00
#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
2019-09-05 00:16:39 +02:00
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;
}
2020-08-13 15:42:49 +02:00
#endif // BAR_WINTITLEACTIONS_PATCH
2019-09-05 00:16:39 +02:00
if (c) {
focus(c);
restack(selmon);
}
}
#endif // STACKER_PATCH
2019-09-05 00:16:39 +02:00
Atom
getatomprop(Client *c, Atom prop)
{
int di;
unsigned long dl;
unsigned char *p = NULL;
Atom da, atom = None;
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
/* 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
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
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)
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
2020-05-31 15:14:29 +02:00
#if KEYMODES_PATCH
grabdefkeys(void)
#else
2019-09-05 00:16:39 +02:00
grabkeys(void)
2020-05-31 15:14:29 +02:00
#endif // KEYMODES_PATCH
2019-09-05 00:16:39 +02:00
{
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);
}
}
void
incnmaster(const Arg *arg)
{
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
#else
2019-09-05 00:16:39 +02:00
selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
2019-09-05 22:58:16 +02:00
#endif // PERTAG_PATCH
2019-09-05 00:16:39 +02:00
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
2020-05-31 15:14:29 +02:00
#if KEYMODES_PATCH
keydefpress(XEvent *e)
#else
2019-09-05 00:16:39 +02:00
keypress(XEvent *e)
2020-05-31 15:14:29 +02:00
#endif // KEYMODES_PATCH
2019-09-05 00:16:39 +02:00
{
unsigned int i;
KeySym keysym;
XKeyEvent *ev;
ev = &e->xkey;
keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
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));
}
void
killclient(const Arg *arg)
{
2019-10-22 18:30:00 +02:00
#if ISPERMANENT_PATCH
if (!selmon->sel || selmon->sel->ispermanent)
#else
2019-09-05 00:16:39 +02:00
if (!selmon->sel)
2019-10-22 18:30:00 +02:00
#endif // ISPERMANENT_PATCH
2019-09-05 00:16:39 +02:00
return;
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) {
#else
2019-09-05 00:16:39 +02:00
if (!sendevent(selmon->sel, wmatom[WMDelete])) {
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
XGrabServer(dpy);
XSetErrorHandler(xerrordummy);
XSetCloseDownMode(dpy, DestroyAll);
XKillClient(dpy, selmon->sel->win);
XSync(dpy, False);
XSetErrorHandler(xerror);
XUngrabServer(dpy);
}
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
selmon->pertag->prevclient[selmon->pertag->curtag] = NULL;
#endif // SWAPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
}
void
manage(Window w, XWindowAttributes *wa)
{
Client *c, *t = NULL;
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
Client *term = NULL;
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
Window trans = None;
XWindowChanges wc;
c = ecalloc(1, sizeof(Client));
c->win = w;
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
c->pid = winpid(w);
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
/* 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
2019-09-05 00:16:39 +02:00
updatetitle(c);
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
c->mon = t->mon;
c->tags = t->tags;
2020-06-23 11:55:55 +02:00
#if FLOATPOS_PATCH
#if SETBORDERPX_PATCH
c->bw = c->mon->borderpx;
#else
c->bw = borderpx;
#endif // SETBORDERPX_PATCH
#endif // FLOATPOS_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_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;
#endif // CENTER_TRANSIENT_WINDOWS_PATCH | CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH
2019-09-05 00:16:39 +02:00
} else {
c->mon = selmon;
2020-06-21 15:33:29 +02:00
#if FLOATPOS_PATCH
#if SETBORDERPX_PATCH
c->bw = c->mon->borderpx;
#else
c->bw = borderpx;
#endif // SETBORDERPX_PATCH
#endif // FLOATPOS_PATCH
2019-09-05 00:16:39 +02:00
applyrules(c);
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
term = termforwin(c);
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
}
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 */
2020-07-18 18:58:39 +02:00
c->y = MAX(c->y, ((c->mon->bar->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
2019-09-05 00:16:39 +02:00
&& (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
2020-06-21 15:33:29 +02:00
#if !FLOATPOS_PATCH
2019-09-14 23:28:04 +02:00
#if SETBORDERPX_PATCH
c->bw = c->mon->borderpx;
#else
2019-09-05 00:16:39 +02:00
c->bw = borderpx;
2019-09-14 23:28:04 +02:00
#endif // SETBORDERPX_PATCH
2020-06-21 15:33:29 +02:00
#endif // FLOATPOS_PATCH
2019-09-05 00:16:39 +02:00
wc.border_width = c->bw;
XConfigureWindow(dpy, w, CWBorderWidth, &wc);
2019-09-11 00:51:37 +02:00
if (c->isfloating)
XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel);
else
XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
2019-09-05 00:16:39 +02:00
configure(c); /* propagates border_width, if size doesn't change */
2020-06-23 11:55:55 +02:00
#if !FLOATPOS_PATCH
updatesizehints(c);
#endif // FLOATPOS_PATCH
if (getatomprop(c, netatom[NetWMState]) == netatom[NetWMFullscreen])
setfullscreen(c, 1);
2019-09-05 00:16:39 +02:00
updatewmhints(c);
2020-08-11 10:17:02 +02:00
#if DECORATION_HINTS_PATCH
updatemotifhints(c);
#endif // DECORATION_HINTS_PATCH
2019-09-08 00:23:30 +02:00
#if CENTER_PATCH
if (c->iscentered) {
2020-06-21 15:33:29 +02:00
c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2;
c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2;
2019-09-08 00:23:30 +02:00
}
#endif // CENTER_PATCH
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
c->sfx = -9999;
c->sfy = -9999;
c->sfw = c->w;
c->sfh = c->h;
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
2019-09-05 00:16:39 +02:00
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
2019-10-04 22:46:47 +02:00
#if MAXIMIZE_PATCH
c->wasfloating = 0;
c->ismax = 0;
2019-10-24 08:47:00 +02:00
#elif EXRESIZE_PATCH
c->wasfloating = 0;
#endif // MAXIMIZE_PATCH / EXRESIZE_PATCH
2019-09-05 00:16:39 +02:00
if (!c->isfloating)
c->isfloating = c->oldstate = trans != None || c->isfixed;
if (c->isfloating) {
2019-09-05 00:16:39 +02:00
XRaiseWindow(dpy, c->win);
2019-09-11 00:51:37 +02:00
XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel);
}
#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH
attachx(c);
#else
2019-09-05 00:16:39 +02:00
attach(c);
#endif
2019-09-05 00:16:39 +02:00
attachstack(c);
XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
(unsigned char *) &(c->win), 1);
XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
2019-09-10 00:18:46 +02:00
2020-08-13 15:42:49 +02:00
#if BAR_WINTITLEACTIONS_PATCH
2019-09-10 00:18:46 +02:00
if (!HIDDEN(c))
setclientstate(c, NormalState);
#else
2019-09-05 00:16:39 +02:00
setclientstate(c, NormalState);
2020-08-13 15:42:49 +02:00
#endif // BAR_WINTITLEACTIONS_PATCH
2019-09-05 00:16:39 +02:00
if (c->mon == selmon)
unfocus(selmon->sel, 0);
c->mon->sel = c;
arrange(c->mon);
2020-08-13 15:42:49 +02:00
#if BAR_WINTITLEACTIONS_PATCH
2019-09-10 00:18:46 +02:00
if (!HIDDEN(c))
XMapWindow(dpy, c->win);
#else
2019-09-05 00:16:39 +02:00
XMapWindow(dpy, c->win);
2020-08-13 15:42:49 +02:00
#endif // BAR_WINTITLEACTIONS_PATCH
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
if (term)
swallow(term, c);
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
focus(NULL);
}
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
2019-09-05 22:10:00 +02:00
Client *i;
if (showsystray && (i = wintosystrayicon(ev->window))) {
sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
2020-07-18 13:03:30 +02:00
drawbarwin(systray->bar);
2019-09-05 22:10:00 +02:00
}
#endif // BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
2019-09-05 00:16:39 +02:00
if (!XGetWindowAttributes(dpy, ev->window, &wa))
return;
if (wa.override_redirect)
return;
if (!wintoclient(ev->window))
manage(ev->window, &wa);
}
2019-09-16 00:32:28 +02:00
#if !FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
void
motionnotify(XEvent *e)
{
static Monitor *mon = NULL;
Monitor *m;
#if LOSEFULLSCREEN_PATCH
Client *sel;
#endif // LOSEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
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);
#else
2019-09-05 00:16:39 +02:00
unfocus(selmon->sel, 1);
selmon = m;
#endif // LOSEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
focus(NULL);
}
mon = m;
}
2019-09-16 00:32:28 +02:00
#endif // FOCUSONCLICK_PATCH
2019-09-05 00:16:39 +02:00
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;
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
if (c->isfullscreen && !c->fakefullscreen) /* no support moving fullscreen windows by mouse */
return;
#else
2019-09-05 00:16:39 +02:00
if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
return;
2019-11-21 12:15:16 +01:00
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-10-08 23:15:11 +02:00
#endif // FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
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;
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))
togglefloating(NULL);
2019-09-07 23:08:53 +02:00
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) {
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
2019-09-05 00:16:39 +02:00
resize(c, nx, ny, c->w, c->h, 1);
2019-09-07 23:08:53 +02:00
/* save last known float coordinates */
c->sfx = nx;
c->sfy = ny;
#else
resize(c, nx, ny, c->w, c->h, 1);
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
}
2020-03-31 08:21:00 +02:00
#if ROUNDED_CORNERS_PATCH
drawroundedcorners(c);
#endif // ROUNDED_CORNERS_PATCH
2019-09-05 00:16:39 +02:00
break;
}
} while (ev.type != ButtonRelease);
XUngrabPointer(dpy, CurrentTime);
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
selmon = m;
focus(NULL);
}
2020-03-31 08:21:00 +02:00
#if ROUNDED_CORNERS_PATCH
drawroundedcorners(c);
#endif // ROUNDED_CORNERS_PATCH
2019-09-05 00:16:39 +02:00
}
Client *
nexttiled(Client *c)
{
2020-08-13 15:42:49 +02:00
#if BAR_WINTITLEACTIONS_PATCH
2019-09-10 00:18:46 +02:00
for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next);
#else
2019-09-05 00:16:39 +02:00
for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
2020-08-13 15:42:49 +02:00
#endif // BAR_WINTITLEACTIONS_PATCH
2019-09-05 00:16:39 +02:00
return c;
}
#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH
2019-09-05 00:16:39 +02:00
void
pop(Client *c)
{
detach(c);
attach(c);
focus(c);
arrange(c->mon);
}
#endif // !ZOOMSWAP_PATCH / TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH
2019-09-05 00:16:39 +02:00
void
propertynotify(XEvent *e)
{
Client *c;
Window trans;
XPropertyEvent *ev = &e->xproperty;
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
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);
2020-07-18 13:03:30 +02:00
drawbarwin(systray->bar);
2019-09-05 22:10:00 +02:00
}
#endif // BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
2019-10-07 23:34:23 +02:00
if ((ev->window == root) && (ev->atom == XA_WM_NAME)) {
#if DWMC_PATCH || FSIGNAL_PATCH
2019-10-07 23:34:23 +02:00
if (!fake_signal())
updatestatus();
#else
2019-09-05 00:16:39 +02:00
updatestatus();
#endif // DWMC_PATCH / FSIGNAL_PATCH
2019-10-07 23:34:23 +02:00
} else if (ev->state == PropertyDelete) {
2019-09-05 00:16:39 +02:00
return; /* ignore */
2019-10-07 23:34:23 +02:00
} else if ((c = wintoclient(ev->window))) {
2019-09-05 00:16:39 +02:00
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);
2019-10-07 00:46:23 +02:00
if (c->isurgent)
2020-07-18 13:03:30 +02:00
drawbars();
2019-09-05 00:16:39 +02:00
break;
}
if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
updatetitle(c);
if (c == c->mon->sel)
drawbar(c->mon);
}
2020-08-11 10:17:02 +02:00
#if DECORATION_HINTS_PATCH
if (ev->atom == motifatom)
updatemotifhints(c);
#endif // DECORATION_HINTS_PATCH
2019-09-05 00:16:39 +02:00
}
}
void
quit(const Arg *arg)
{
2020-08-10 10:22:21 +02:00
#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);
2020-08-10 10:22:21 +02:00
#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
2019-09-05 00:16:39 +02:00
running = 0;
#endif // ONLYQUITONEMPTY_PATCH
2020-08-10 10:22:21 +02:00
#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
2019-09-05 00:16:39 +02:00
}
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;
2019-10-24 08:47:00 +02:00
#if EXRESIZE_PATCH
c->expandmask = 0;
#endif // EXRESIZE_PATCH
2019-09-05 00:16:39 +02:00
wc.border_width = c->bw;
2019-10-04 23:39:01 +02:00
#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
) && !c->isfullscreen && !c->isfloating
&& c->mon->lt[c->mon->sellt]->arrange) {
2019-10-04 23:39:01 +02:00
c->w = wc.width += c->bw * 2;
c->h = wc.height += c->bw * 2;
wc.border_width = 0;
}
#endif // NOBORDER_PATCH
2019-09-05 00:16:39 +02:00
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
2020-06-24 14:49:30 +02:00
int opx, opy, och, ocw, nx, ny;
int horizcorner, vertcorner;
unsigned int dui;
Window dummy;
#endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH
2019-09-05 00:16:39 +02:00
Client *c;
Monitor *m;
XEvent ev;
Time lasttime = 0;
if (!(c = selmon->sel))
return;
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
if (c->isfullscreen && !c->fakefullscreen) /* no support resizing fullscreen windows by mouse */
return;
#else
2019-09-05 00:16:39 +02:00
if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
return;
2019-11-21 12:15:16 +01:00
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-10-08 23:15:11 +02:00
#endif // !FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
restack(selmon);
ocx = c->x;
ocy = c->y;
2020-06-24 14:49:30 +02:00
#if RESIZEPOINT_PATCH
och = c->h;
ocw = c->w;
#elif RESIZECORNERS_PATCH
och = c->y + c->h;
ocw = c->x + c->w;
2020-06-24 14:49:30 +02:00
#endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH
#if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH
2020-06-24 14:49:30 +02:00
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
2019-09-07 22:35:37 +02:00
XWarpPointer (dpy, None, c->win, 0, 0, 0, 0,
2019-10-24 06:52:00 +02:00
horizcorner ? (-c->bw) : (c->w + c->bw - 1),
vertcorner ? (-c->bw) : (c->h + c->bw - 1));
#endif // RESIZECORNERS_PATCH
2019-09-07 22:35:37 +02:00
#else
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
return;
2019-09-05 00:16:39 +02:00
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
2020-06-24 14:49:30 +02:00
#endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH
2019-09-05 00:16:39 +02:00
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;
2020-06-24 14:49:30 +02:00
#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
2019-09-07 22:35:37 +02:00
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);
2020-06-24 14:49:30 +02:00
#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
2019-09-05 00:16:39 +02:00
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))
togglefloating(NULL);
}
2019-09-07 22:35:37 +02:00
if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) {
2020-06-24 14:49:30 +02:00
#if RESIZECORNERS_PATCH || RESIZEPOINT_PATCH
resizeclient(c, nx, ny, nw, nh);
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
/* save last known float dimensions */
c->sfx = nx;
c->sfy = ny;
c->sfw = nw;
c->sfh = nh;
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
2019-09-07 22:35:37 +02:00
#else
2019-09-05 00:16:39 +02:00
resize(c, c->x, c->y, nw, nh, 1);
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
c->sfx = c->x;
c->sfy = c->y;
c->sfw = nw;
c->sfh = nh;
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
2019-09-07 22:35:37 +02:00
#endif // RESIZECORNERS_PATCH
2020-03-31 08:21:00 +02:00
#if ROUNDED_CORNERS_PATCH
drawroundedcorners(c);
#endif // ROUNDED_CORNERS_PATCH
2019-09-07 22:35:37 +02:00
}
2019-09-05 00:16:39 +02:00
break;
}
} while (ev.type != ButtonRelease);
2020-06-24 14:49:30 +02:00
#if !RESIZEPOINT_PATCH
2019-09-07 22:35:37 +02:00
#if RESIZECORNERS_PATCH
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
2019-10-24 06:52:00 +02:00
horizcorner ? (-c->bw) : (c->w + c->bw - 1),
vertcorner ? (-c->bw) : (c->h + c->bw - 1));
2019-09-07 22:35:37 +02:00
#else
2019-09-05 00:16:39 +02:00
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
2019-09-07 22:35:37 +02:00
#endif // RESIZECORNERS_PATCH
2020-06-24 14:49:30 +02:00
#endif // RESIZEPOINT_PATCH
2019-09-05 00:16:39 +02:00
XUngrabPointer(dpy, CurrentTime);
while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
sendmon(c, m);
selmon = m;
focus(NULL);
}
}
void
restack(Monitor *m)
{
Client *c;
XEvent ev;
XWindowChanges wc;
drawbar(m);
if (!m->sel)
return;
if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
XRaiseWindow(dpy, m->sel->win);
if (m->lt[m->sellt]->arrange) {
wc.stack_mode = Below;
2020-07-18 18:58:39 +02:00
wc.sibling = m->bar->win;
2019-09-05 00:16:39 +02:00
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));
2019-09-15 00:43:35 +02:00
#if WARP_PATCH
if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && selmon->lt[selmon->sellt] != &layouts[MONOCLE_LAYOUT_POS])
2019-09-15 00:43:35 +02:00
warp(m->sel);
#endif // WARP_PATCH
2019-09-05 00:16:39 +02:00
}
void
run(void)
{
XEvent ev;
/* main event loop */
XSync(dpy, False);
while (running && !XNextEvent(dpy, &ev))
if (handler[ev.type])
handler[ev.type](&ev); /* call handler */
}
void
scan(void)
{
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 (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
manage(wins[i], &wa);
}
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);
2019-09-05 00:16:39 +02:00
}
}
void
sendmon(Client *c, Monitor *m)
{
2019-10-24 08:47:00 +02:00
#if EXRESIZE_PATCH
Monitor *oldm = selmon;
#endif // EXRESIZE_PATCH
2019-09-05 00:16:39 +02:00
if (c->mon == m)
return;
#if SENDMON_KEEPFOCUS_PATCH && !EXRESIZE_PATCH
int hadfocus = (c == selmon->sel);
#endif // SENDMON_KEEPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
unfocus(c, 1);
detach(c);
detachstack(c);
2020-06-24 17:12:54 +02:00
#if SENDMON_KEEPFOCUS_PATCH && !EXRESIZE_PATCH
arrange(c->mon);
#endif // SENDMON_KEEPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
c->mon = m;
#if EMPTYVIEW_PATCH
c->tags = (m->tagset[m->seltags] ? m->tagset[m->seltags] : 1);
#else
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
attach(c);
#endif
2019-09-05 00:16:39 +02:00
attachstack(c);
2019-10-24 08:47:00 +02:00
#if EXRESIZE_PATCH
if (oldm != m)
arrange(oldm);
arrange(m);
focus(c);
restack(m);
2020-06-24 17:12:54 +02:00
#elif SENDMON_KEEPFOCUS_PATCH
arrange(m);
if (hadfocus) {
focus(c);
restack(m);
} else
focus(NULL);
2019-10-24 08:47:00 +02:00
#else
2019-09-05 00:16:39 +02:00
focus(NULL);
arrange(NULL);
2020-06-24 17:12:54 +02:00
#endif // EXRESIZE_PATCH / SENDMON_KEEPFOCUS_PATCH
#if SWITCHTAG_PATCH
if (c->switchtag)
c->switchtag = 0;
#endif // SWITCHTAG_PATCH
2019-09-05 00:16:39 +02:00
}
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
2019-09-05 22:10:00 +02:00
sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
#else
2019-09-05 00:16:39 +02:00
sendevent(Client *c, Atom proto)
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
{
int n;
Atom *protocols;
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
Atom mt;
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
int exists = 0;
XEvent ev;
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
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
2019-09-05 00:16:39 +02:00
if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
while (!exists && n--)
exists = protocols[n] == proto;
XFree(protocols);
}
#endif // BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
2019-09-05 00:16:39 +02:00
if (exists) {
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
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
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
}
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 BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
#else
2019-09-05 00:16:39 +02:00
sendevent(c, wmatom[WMTakeFocus]);
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
}
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;
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
if (!c->fakefullscreen) {
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-09-05 00:16:39 +02:00
c->oldstate = c->isfloating;
c->oldbw = c->bw;
c->bw = 0;
c->isfloating = 1;
resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
XRaiseWindow(dpy, c->win);
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
}
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-10-08 23:15:11 +02:00
#endif // !FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
} else if (!fullscreen && c->isfullscreen){
XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
PropModeReplace, (unsigned char*)0, 0);
c->isfullscreen = 0;
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
if (!c->fakefullscreen) {
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-09-05 00:16:39 +02:00
c->isfloating = c->oldstate;
c->bw = c->oldbw;
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);
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
}
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-10-08 23:15:11 +02:00
#endif // !FAKEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
}
}
void
setlayout(const Arg *arg)
{
2019-09-05 22:58:16 +02:00
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) {
#if PERTAG_PATCH
selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
#else
2019-09-05 00:16:39 +02:00
selmon->sellt ^= 1;
2019-09-05 22:58:16 +02:00
#endif // PERTAG_PATCH
2019-10-24 08:47:00 +02:00
#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
2019-09-05 22:58:16 +02:00
}
2019-09-05 00:16:39 +02:00
if (arg && arg->v)
2019-09-05 22:58:16 +02:00
#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
2019-09-05 00:16:39 +02:00
selmon->lt[selmon->sellt] = (Layout *)arg->v;
2019-09-05 22:58:16 +02:00
#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
2019-09-05 00:16:39 +02:00
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)
2019-09-05 00:16:39 +02:00
return;
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
#else
2019-09-05 00:16:39 +02:00
selmon->mfact = f;
2019-09-05 22:58:16 +02:00
#endif // PERTAG_PATCH
2019-09-05 00:16:39 +02:00
arrange(selmon);
}
void
setup(void)
{
int i;
XSetWindowAttributes wa;
Atom utf8string;
/* clean up any zombies immediately */
sigchld(0);
#if RESTARTSIG_PATCH
signal(SIGHUP, sighup);
signal(SIGTERM, sigterm);
#endif // RESTARTSIG_PATCH
2019-09-05 00:16:39 +02:00
/* init screen */
screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen);
sh = DisplayHeight(dpy, screen);
root = RootWindow(dpy, screen);
#if BAR_ALPHA_PATCH
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
#endif // BAR_PANGO_PATCH
2019-09-05 00:16:39 +02:00
die("no fonts could be loaded.");
2020-07-19 16:57:30 +02:00
#if BAR_STATUSPADDING_PATCH
2019-09-07 23:29:37 +02:00
lrpad = drw->fonts->h + horizpadbar;
bh = drw->fonts->h + vertpadbar;
#else
2019-09-05 00:16:39 +02:00
lrpad = drw->fonts->h;
2020-06-21 20:04:56 +02:00
#if BAR_HEIGHT_PATCH
bh = bar_height ? bar_height : drw->fonts->h + 2;
#else
2019-09-05 00:16:39 +02:00
bh = drw->fonts->h + 2;
2020-06-21 20:04:56 +02:00
#endif // BAR_HEIGHT_PATCH
#endif // BAR_STATUSPADDING_PATCH
2019-09-05 00:16:39 +02:00
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);
2019-09-07 23:46:08 +02:00
#if WINDOWROLERULE_PATCH
wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
#endif // WINDOWROLERULE_PATCH
2019-09-05 00:16:39 +02:00
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
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
2019-09-12 00:48:29 +02:00
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
2019-09-05 00:16:39 +02:00
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);
2020-08-11 10:17:02 +02:00
#if DECORATION_HINTS_PATCH
motifatom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
#endif // DECORATION_HINTS_PATCH
2019-09-05 00:16:39 +02:00
/* 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
2019-09-05 00:16:39 +02:00
cursor[CurMove] = drw_cur_create(drw, XC_fleur);
/* init appearance */
#if BAR_VTCOLORS_PATCH
2020-02-11 18:31:11 +01:00
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
2020-05-26 20:53:53 +02:00
scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *));
#if BAR_ALPHA_PATCH
2020-05-26 20:53:53 +02:00
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
2020-05-26 20:53:53 +02:00
#else
2019-09-05 00:16:39 +02:00
scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
#endif // BAR_STATUS2D_PATCH
2019-09-05 00:16:39 +02:00
for (i = 0; i < LENGTH(colors); i++)
#if BAR_ALPHA_PATCH
2020-05-26 20:53:53 +02:00
scheme[i] = drw_scm_create(drw, colors[i], alphas[i], ColCount);
2019-09-11 00:51:37 +02:00
#else
2020-05-26 20:53:53 +02:00
scheme[i] = drw_scm_create(drw, colors[i], ColCount);
#endif // BAR_ALPHA_PATCH
2020-07-19 16:57:30 +02:00
#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
2019-09-05 00:16:39 +02:00
updatebars();
updatestatus();
2019-09-05 00:16:39 +02:00
/* 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);
XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
PropModeReplace, (unsigned char *) "dwm", 3);
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
2019-09-12 00:48:29 +02:00
setnumdesktops();
setcurrentdesktop();
setdesktopnames();
setviewport();
#endif // BAR_EWMHTAGS_PATCH
2019-09-05 00:16:39 +02:00
XDeleteProperty(dpy, root, netatom[NetClientList]);
/* 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);
grabkeys();
focus(NULL);
}
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 && !SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
[dwm][PATCH] Multiple scratchpads This patch enables multiple scratchpads, each with one assigned window. This enables the same scratchpad workflow that you have in i3. Scratchpads are implemented as special tags, whose mask does not apply to new spawned windows. To assign a window to a scratchpad you have to set up a rule, as you do with regular tags. Windows tagged with scratchpad tags can be set floating or not in the rules array. Most users would probably want them floating (i3 style), but having them tiled does also perfectly work and might fit better the DWM approach. In case they are set floating, the patch moves them to the center of the screen whenever the are shown. The patch can easily be modified to make this last feature configurable in the rules array (see the center patch). The togglescratch function, borrowed from the previous scratchpad patch and slightly modified, can be used to spawn a registered scratchpad process or toggle its view. This function looks for a window tagged on the selected scratchpad. If it is found its view is toggled. If it is not found the corresponding registered command is spawned. The config.def.h shows three examples of its use to spawn a terminal in the first scratchpad tag, a second terminal running ranger on the second scratchpad tag and the keepassxc application to manage passwords on a third scratchpad tag. If you prefer to spawn your scratchpad applications from the startup script, you might opt for binding keys to toggleview instead, as scratchpads are just special tags (you may even extend the TAGKEYS macro to generalize the key bindings).
2020-04-16 16:39:22 +02:00
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_PATCH | SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH
2019-09-05 00:16:39 +02:00
/* show clients top down */
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
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;
}
2019-10-24 08:47:00 +02:00
#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
2019-09-05 00:16:39 +02:00
XMoveWindow(dpy, c->win, c->x, c->y);
#endif // AUTORESIZE_PATCH
2019-10-08 23:15:11 +02:00
if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating)
#if !FAKEFULLSCREEN_PATCH
&& !c->isfullscreen
#endif // !FAKEFULLSCREEN_PATCH
)
2019-09-05 00:16:39 +02:00
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)
{
2020-08-10 10:22:21 +02:00
#if COOL_AUTOSTART_PATCH
pid_t pid;
#endif // COOL_AUTOSTART_PATCH
2019-09-05 00:16:39 +02:00
if (signal(SIGCHLD, sigchld) == SIG_ERR)
die("can't install SIGCHLD handler:");
2020-08-10 10:22:21 +02:00
#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
2019-09-05 00:16:39 +02:00
while (0 < waitpid(-1, NULL, WNOHANG));
2020-08-10 10:22:21 +02:00
#endif // COOL_AUTOSTART_PATCH
2019-09-05 00:16:39 +02:00
}
void
spawn(const Arg *arg)
{
#if BAR_STATUSCMD_PATCH && !BAR_DWMBLOCKS_PATCH
2020-04-13 14:59:58 +02:00
char *cmd = NULL;
#endif // BAR_STATUSCMD_PATCH | BAR_DWMBLOCKS_PATCH
2019-10-24 08:03:00 +02:00
#if !NODMENU_PATCH
2019-09-05 00:16:39 +02:00
if (arg->v == dmenucmd)
dmenumon[0] = '0' + selmon->num;
2019-10-24 08:03:00 +02:00
#endif // NODMENU_PATCH
#if BAR_STATUSCMD_PATCH && !BAR_DWMBLOCKS_PATCH
2020-04-13 14:59:58 +02:00
#if !NODMENU_PATCH
else if (arg->v == statuscmd)
#else
if (arg->v == statuscmd)
#endif // NODMENU_PATCH
{
int len = strlen(statuscmds[statuscmdn]) + 1;
if (!(cmd = malloc(sizeof(char)*len + sizeof(statusexport))))
die("malloc:");
strcpy(cmd, statusexport);
strcat(cmd, statuscmds[statuscmdn]);
cmd[LENGTH(statusexport)-3] = '0' + lastbutton;
statuscmd[2] = cmd;
}
#endif // BAR_STATUSCMD_PATCH | BAR_DWMBLOCKS_PATCH
2020-04-13 14:59:58 +02:00
2019-09-05 00:16:39 +02:00
if (fork() == 0) {
if (dpy)
close(ConnectionNumber(dpy));
2019-10-10 23:50:30 +02:00
#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
2019-09-05 00:16:39 +02:00
setsid();
execvp(((char **)arg->v)[0], (char **)arg->v);
fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
perror(" failed");
exit(EXIT_SUCCESS);
}
#if BAR_STATUSCMD_PATCH && !BAR_DWMBLOCKS_PATCH
2020-04-13 14:59:58 +02:00
free(cmd);
#endif // BAR_STATUSCMD_PATCH | BAR_DWMBLOCKS_PATCH
2019-09-05 00:16:39 +02:00
}
void
tag(const Arg *arg)
{
#if SWAPFOCUS_PATCH && PERTAG_PATCH
unsigned int tagmask, tagindex;
#endif // SWAPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
focus(NULL);
2020-01-29 08:31:00 +01:00
#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;
2020-01-29 08:31:00 +01:00
#endif // SWAPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
arrange(selmon);
2019-09-15 23:42:47 +02:00
#if VIEWONTAG_PATCH
view(arg);
#endif // VIEWONTAG_PATCH
2019-09-05 00:16:39 +02:00
}
}
void
tagmon(const Arg *arg)
{
2019-09-05 23:39:25 +02:00
#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) {
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));
2019-09-05 23:39:25 +02:00
#else
2019-09-05 00:16:39 +02:00
if (!selmon->sel || !mons->next)
return;
sendmon(selmon->sel, dirtomon(arg->i));
2019-09-05 23:39:25 +02:00
#endif // TAGMONFIXFS_PATCH
2019-09-05 00:16:39 +02:00
}
void
togglebar(const Arg *arg)
{
2020-07-18 18:58:39 +02:00
Bar *bar;
#if BAR_HOLDBAR_PATCH && PERTAG_PATCH && PERTAGBAR_PATCH
2020-06-30 10:15:36 +02:00
selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = (selmon->showbar == 2 ? 1 : !selmon->showbar);
#elif BAR_HOLDBAR_PATCH
2020-06-30 10:15:36 +02:00
selmon->showbar = (selmon->showbar == 2 ? 1 : !selmon->showbar);
#elif PERTAG_PATCH && PERTAGBAR_PATCH
2019-09-05 22:58:16 +02:00
selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
#else
2019-09-05 00:16:39 +02:00
selmon->showbar = !selmon->showbar;
#endif // BAR_HOLDBAR_PATCH | PERTAG_PATCH
2019-09-05 00:16:39 +02:00
updatebarpos(selmon);
2020-07-18 18:58:39 +02:00
for (bar = selmon->bar; bar; bar = bar->next)
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
2019-09-05 00:16:39 +02:00
arrange(selmon);
}
void
togglefloating(const Arg *arg)
{
Client *c = selmon->sel;
if (arg && arg->v)
c = (Client*)arg->v;
if (!c)
2019-09-05 00:16:39 +02:00
return;
2019-10-08 23:15:11 +02:00
#if !FAKEFULLSCREEN_PATCH
2019-11-21 12:15:16 +01:00
#if FAKEFULLSCREEN_CLIENT_PATCH
if (c->isfullscreen && !c->fakefullscreen) /* no support for fullscreen windows */
2019-11-21 12:15:16 +01:00
return;
#else
if (c->isfullscreen) /* no support for fullscreen windows */
2019-09-05 00:16:39 +02:00
return;
2019-11-21 12:15:16 +01:00
#endif // FAKEFULLSCREEN_CLIENT_PATCH
2019-10-08 23:15:11 +02:00
#endif // !FAKEFULLSCREEN_PATCH
c->isfloating = !c->isfloating || c->isfixed;
if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
2019-09-11 00:51:37 +02:00
else
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
if (c->isfloating) {
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
if (c->sfx != -9999) {
2019-09-07 23:08:53 +02:00
/* restore last known float dimensions */
resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0);
arrange(c->mon);
2019-09-07 23:08:53 +02:00
return;
}
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH // EXRESIZE_PATCH
resize(c, c->x, c->y, c->w, c->h, 0);
2019-10-24 08:47:00 +02:00
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
} else {
/* save last known float dimensions */
c->sfx = c->x;
c->sfy = c->y;
c->sfw = c->w;
c->sfh = c->h;
2019-10-24 08:47:00 +02:00
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
2019-09-07 23:08:53 +02:00
}
arrange(c->mon);
2019-09-05 00:16:39 +02:00
}
void
toggletag(const Arg *arg)
{
unsigned int newtags;
#if SWAPFOCUS_PATCH && PERTAG_PATCH
unsigned int tagmask, tagindex;
#endif // SWAPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
arrange(selmon);
}
#if BAR_EWMHTAGS_PATCH
2019-09-12 00:48:29 +02:00
updatecurrentdesktop();
#endif // BAR_EWMHTAGS_PATCH
2019-09-05 00:16:39 +02:00
}
void
toggleview(const Arg *arg)
{
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
int i;
#endif // PERTAG_PATCH
2019-09-05 00:16:39 +02:00
2019-10-05 00:12:52 +02:00
#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;
2019-10-05 00:12:52 +02:00
// 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]);
2019-10-05 00:12:52 +02:00
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
2019-09-05 00:16:39 +02:00
if (newtagset) {
#endif // EMPTYVIEW_PATCH
2019-09-05 00:16:39 +02:00
selmon->tagset[selmon->seltags] = newtagset;
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
if (newtagset == ~0) {
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
2019-09-05 00:16:39 +02:00
focus(NULL);
arrange(selmon);
#if !EMPTYVIEW_PATCH
2019-09-05 00:16:39 +02:00
}
#endif // EMPTYVIEW_PATCH
#if BAR_EWMHTAGS_PATCH
2019-09-12 00:48:29 +02:00
updatecurrentdesktop();
#endif // BAR_EWMHTAGS_PATCH
2019-09-05 00:16:39 +02:00
}
void
unfocus(Client *c, int setfocus)
{
if (!c)
return;
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
selmon->pertag->prevclient[selmon->pertag->curtag] = c;
#endif // SWAPFOCUS_PATCH
2020-04-05 09:10:00 +02:00
#if LOSEFULLSCREEN_PATCH
#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH
if (c->isfullscreen && !c->fakefullscreen && ISVISIBLE(c) && c->mon == selmon)
2020-04-05 09:10:00 +02:00
setfullscreen(c, 0);
#else
if (c->isfullscreen && ISVISIBLE(c) && c->mon == selmon)
2020-04-05 09:10:00 +02:00
setfullscreen(c, 0);
#endif // FAKEFULLSCREEN_CLIENT_PATCH
#endif // LOSEFULLSCREEN_PATCH
2019-09-05 00:16:39 +02:00
grabbuttons(c, 0);
2019-09-11 00:51:37 +02:00
if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
XWindowChanges wc;
2019-10-22 19:08:00 +02:00
#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
2019-09-05 00:16:39 +02:00
detach(c);
detachstack(c);
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);
}
free(c);
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
if (s)
return;
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
focus(NULL);
updateclientlist();
arrange(m);
2019-11-26 20:23:58 +01:00
#if SWITCHTAG_PATCH
if (switchtag)
view(&((Arg) { .ui = switchtag }));
2019-11-26 20:23:58 +01:00
#endif // SWITCHTAG_PATCH
2019-09-05 00:16:39 +02:00
}
void
unmapnotify(XEvent *e)
{
Client *c;
XUnmapEvent *ev = &e->xunmap;
if ((c = wintoclient(ev->window))) {
if (ev->send_event)
setclientstate(c, WithdrawnState);
else
unmanage(c, 0);
#if BAR_SYSTRAY_PATCH
2019-09-05 22:10:00 +02:00
} 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);
2020-07-18 13:03:30 +02:00
drawbarwin(systray->bar);
#endif // BAR_SYSTRAY_PATCH
2019-09-05 00:16:39 +02:00
}
}
void
updatebars(void)
{
2020-07-18 18:58:39 +02:00
Bar *bar;
2019-09-05 00:16:39 +02:00
Monitor *m;
XSetWindowAttributes wa = {
.override_redirect = True,
#if BAR_ALPHA_PATCH
2019-09-05 00:16:39 +02:00
.background_pixel = 0,
.border_pixel = 0,
.colormap = cmap,
#else
.background_pixmap = ParentRelative,
#endif // BAR_ALPHA_PATCH
2019-09-05 00:16:39 +02:00
.event_mask = ButtonPressMask|ExposureMask
};
XClassHint ch = {"dwm", "dwm"};
for (m = mons; m; m = m->next) {
2020-07-18 18:58:39 +02:00
for (bar = m->bar; bar; bar = bar->next) {
if (!bar->win) {
#if BAR_ALPHA_PATCH
2020-07-18 18:58:39 +02:00
bar->win = XCreateWindow(dpy, root, bar->bx, bar->by, bar->bw, bar->bh, 0, depth,
InputOutput, visual,
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa);
#else
2020-07-18 18:58:39 +02:00
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
2020-07-18 18:58:39 +02:00
XDefineCursor(dpy, bar->win, cursor[CurNormal]->cursor);
XMapRaised(dpy, bar->win);
XSetClassHint(dpy, bar->win, &ch);
}
2019-10-24 07:35:00 +02:00
}
2019-09-05 00:16:39 +02:00
}
}
void
updatebarpos(Monitor *m)
{
2020-08-10 12:32:58 +02:00
m->wx = m->mx;
2019-09-05 00:16:39 +02:00
m->wy = m->my;
2020-08-10 12:32:58 +02:00
m->ww = m->mw;
2019-09-05 00:16:39 +02:00
m->wh = m->mh;
int num_bars;
2020-07-18 18:58:39 +02:00
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
2020-08-10 12:32:58 +02:00
#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) {
2020-08-10 12:32:58 +02:00
bar->bx = m->wx + x_pad;
bar->bw = m->ww - 2 * x_pad;
bar->bh = bh;
}
for (bar = m->bar; bar; bar = bar->next)
if (!m->showbar || !bar->showbar)
2020-07-21 11:35:26 +02:00
bar->by = -bh - y_pad;
if (!m->showbar)
2020-07-21 11:35:26 +02:00
return;
for (num_bars = 0, bar = m->bar; bar; bar = bar->next) {
if (!bar->showbar)
continue;
2020-07-21 11:35:26 +02:00
if (bar->topbar)
2020-08-10 12:32:58 +02:00
m->wy = m->wy + bh + y_pad;
num_bars++;
}
2020-07-21 11:35:26 +02:00
m->wh = m->wh - y_pad * num_bars - bh * num_bars;
for (bar = m->bar; bar; bar = bar->next)
2020-07-21 11:49:59 +02:00
bar->by = (bar->topbar ? m->wy - bh : m->wy + m->wh);
2019-09-05 00:16:39 +02:00
}
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);
}
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;
2019-10-07 22:56:53 +02:00
#if SORTSCREENS_PATCH
sortscreens(unique, nn);
#endif // SORTSCREENS_PATCH
2019-09-05 00:16:39 +02:00
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();
}
2020-06-10 11:52:35 +02:00
for (i = 0, m = mons; i < nn && m; m = m->next, i++) {
2019-09-05 00:16:39 +02:00
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);
}
2020-06-10 11:52:35 +02:00
}
2019-09-05 00:16:39 +02:00
} 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;
2019-09-05 00:16:39 +02:00
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 */
2020-06-15 21:01:05 +02:00
#if SIZEHINTS_PATCH || SIZEHINTS_RULED_PATCH
size.flags = 0;
#else
2019-09-05 00:16:39 +02:00
size.flags = PSize;
2020-06-15 21:01:05 +02:00
#endif // SIZEHINTS_PATCH | SIZEHINTS_RULED_PATCH
2019-09-05 00:16:39 +02:00
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;
2020-06-15 21:01:05 +02:00
#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
2019-09-05 00:16:39 +02:00
c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
}
void
updatestatus(void)
{
2020-07-18 13:03:30 +02:00
Monitor *m;
2020-07-18 21:12:30 +02:00
#if BAR_EXTRASTATUS_PATCH
2020-07-05 21:53:36 +02:00
if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext))) {
2019-10-24 07:35:00 +02:00
strcpy(stext, "dwm-"VERSION);
estext[0] = '\0';
} else {
2020-07-05 21:53:36 +02:00
char *e = strchr(rawstext, statussep);
2019-10-24 07:35:00 +02:00
if (e) {
*e = '\0'; e++;
#if BAR_STATUSCMD_PATCH
2020-07-05 21:53:36 +02:00
strncpy(rawestext, e, sizeof(estext) - 1);
copyvalidchars(estext, rawestext);
#else
2019-10-24 07:35:00 +02:00
strncpy(estext, e, sizeof(estext) - 1);
#endif // BAR_STATUSCMD_PATCH
2019-10-24 07:35:00 +02:00
} else {
estext[0] = '\0';
}
#if BAR_STATUSCMD_PATCH
2020-07-05 21:53:36 +02:00
copyvalidchars(stext, rawstext);
2020-04-13 14:59:58 +02:00
#else
2020-07-05 21:53:36 +02:00
strncpy(stext, rawstext, sizeof(stext) - 1);
#endif // BAR_STATUSCMD_PATCH
2019-10-24 07:35:00 +02:00
}
#elif BAR_STATUSCMD_PATCH
2020-04-13 14:59:58 +02:00
if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext)))
strcpy(stext, "dwm-"VERSION);
else
copyvalidchars(stext, rawstext);
2019-10-24 07:35:00 +02:00
#else
2019-09-05 00:16:39 +02:00
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION);
2020-07-18 21:12:30 +02:00
#endif // BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH
for (m = mons; m; m = m->next)
drawbar(m);
2019-09-05 00:16:39 +02:00
}
void
updatetitle(Client *c)
{
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);
}
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);
2020-07-18 13:03:30 +02:00
} else
2019-09-05 00:16:39 +02:00
c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
2020-07-18 13:03:30 +02:00
if (c->isurgent) {
if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeUrg][ColBorder].pixel);
2019-09-16 00:13:03 +02:00
}
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
#endif // EMPTYVIEW_PATCH
2019-09-05 00:16:39 +02:00
return;
selmon->seltags ^= 1; /* toggle sel tagset */
2019-09-05 22:58:16 +02:00
#if PERTAG_PATCH
pertagview(arg);
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH
Client *unmodified = selmon->pertag->prevclient[selmon->pertag->curtag];
#endif // SWAPFOCUS_PATCH
2019-09-05 22:58:16 +02:00
#else
2019-09-05 00:16:39 +02:00
if (arg->ui & TAGMASK)
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2019-09-05 22:58:16 +02:00
#endif // PERTAG_PATCH
2019-09-05 00:16:39 +02:00
focus(NULL);
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
selmon->pertag->prevclient[selmon->pertag->curtag] = unmodified;
#endif // SWAPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
arrange(selmon);
#if BAR_EWMHTAGS_PATCH
2019-09-12 00:48:29 +02:00
updatecurrentdesktop();
#endif // BAR_EWMHTAGS_PATCH
2019-09-05 00:16:39 +02:00
}
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;
2020-07-18 18:58:39 +02:00
Bar *bar;
2019-09-05 00:16:39 +02:00
if (w == root && getrootptr(&x, &y))
return recttomon(x, y, 1, 1);
for (m = mons; m; m = m->next)
2020-07-18 18:58:39 +02:00
for (bar = m->bar; bar; bar = bar->next)
if (w == bar->win)
return m;
2019-09-05 00:16:39 +02:00
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
2019-09-05 00:16:39 +02:00
#if ZOOMFLOATING_PATCH
if (c && c->isfloating)
togglefloating(&((Arg) { .v = c }));
#endif // ZOOMFLOATING_PATCH
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
c->mon->pertag->prevclient[c->mon->pertag->curtag] = nexttiled(c->mon->clients);
2020-01-29 08:31:00 +01:00
#endif // SWAPFOCUS_PATCH
if (!c->mon->lt[c->mon->sellt]->arrange
|| (c && c->isfloating)
#if ZOOMSWAP_PATCH
|| !c
#endif // ZOOMSWAP_PATCH
)
2019-09-05 00:16:39 +02:00
return;
#if ZOOMSWAP_PATCH
if (c == nexttiled(c->mon->clients)) {
2020-01-15 13:21:56 +01:00
#if PERTAG_PATCH
p = c->mon->pertag->prevzooms[c->mon->pertag->curtag];
2020-01-15 13:21:56 +01:00
#else
p = prevzoom;
#endif // PERTAG_PATCH
at = findbefore(p);
2020-01-15 13:21:56 +01:00
if (at)
cprevious = nexttiled(at->next);
if (!cprevious || cprevious != p) {
#if PERTAG_PATCH
c->mon->pertag->prevzooms[c->mon->pertag->curtag] = NULL;
#else
2020-01-15 13:21:56 +01:00
prevzoom = NULL;
#endif // PERTAG_PATCH
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
if (!c || !(c = c->mon->pertag->prevclient[c->mon->pertag->curtag] = nexttiled(c->next)))
2020-01-29 08:31:00 +01:00
#else
2020-01-15 13:21:56 +01:00
if (!c || !(c = nexttiled(c->next)))
2020-01-29 08:31:00 +01:00
#endif // SWAPFOCUS_PATCH
2020-01-15 13:21:56 +01:00
return;
} else
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
c = c->mon->pertag->prevclient[c->mon->pertag->curtag] = cprevious;
2020-01-29 08:31:00 +01:00
#else
2020-01-15 13:21:56 +01:00
c = cprevious;
2020-01-29 08:31:00 +01:00
#endif // SWAPFOCUS_PATCH
2020-01-15 13:21:56 +01:00
}
cold = nexttiled(c->mon->clients);
2020-01-15 13:21:56 +01:00
if (c != cold && !at)
at = findbefore(c);
2020-01-15 13:21:56 +01:00
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;
2020-01-15 13:21:56 +01:00
#else
prevzoom = cold;
#endif // PERTAG_PATCH
if (cold && at != cold) {
detach(cold);
cold->next = at->next;
at->next = cold;
}
2020-01-15 13:21:56 +01:00
}
focus(c);
arrange(c->mon);
#else
if (c == nexttiled(c->mon->clients))
2020-01-29 08:31:00 +01:00
#if SWAPFOCUS_PATCH && PERTAG_PATCH
if (!c || !(c = c->mon->pertag->prevclient[c->mon->pertag->curtag] = nexttiled(c->next)))
2020-01-29 08:31:00 +01:00
#else
2019-09-05 00:16:39 +02:00
if (!c || !(c = nexttiled(c->next)))
2020-01-29 08:31:00 +01:00
#endif // SWAPFOCUS_PATCH
2019-09-05 00:16:39 +02:00
return;
pop(c);
#endif // ZOOMSWAP_PATCH
2019-09-05 00:16:39 +02:00
}
int
main(int argc, char *argv[])
{
#if CMDCUSTOMIZE_PATCH
2019-10-05 23:56:43 +02:00
for (int i=1;i<argc;i+=1)
if (!strcmp("-v", argv[i]))
die("dwm-"VERSION);
else if (!strcmp("-h", argv[i]) || !strcmp("--help", argv[i]))
die(help());
else if (!strcmp("-fn", argv[i])) /* font set */
#if BAR_PANGO_PATCH
strcpy(font, argv[++i]);
#else
2019-10-05 23:56:43 +02:00
fonts[0] = argv[++i];
#endif // BAR_PANGO_PATCH
#if !BAR_VTCOLORS_PATCH
2019-10-05 23:56:43 +02:00
else if (!strcmp("-nb", argv[i])) /* normal background color */
colors[SchemeNorm][1] = argv[++i];
else if (!strcmp("-nf", argv[i])) /* normal foreground color */
colors[SchemeNorm][0] = argv[++i];
else if (!strcmp("-sb", argv[i])) /* selected background color */
colors[SchemeSel][1] = argv[++i];
else if (!strcmp("-sf", argv[i])) /* selected foreground color */
colors[SchemeSel][0] = argv[++i];
#endif // !BAR_VTCOLORS_PATCH
#if NODMENU_PATCH
else if (!strcmp("-df", argv[i])) /* dmenu font */
dmenucmd[2] = argv[++i];
else if (!strcmp("-dnb", argv[i])) /* dmenu normal background color */
dmenucmd[4] = argv[++i];
else if (!strcmp("-dnf", argv[i])) /* dmenu normal foreground color */
dmenucmd[6] = argv[++i];
else if (!strcmp("-dsb", argv[i])) /* dmenu selected background color */
dmenucmd[8] = argv[++i];
else if (!strcmp("-dsf", argv[i])) /* dmenu selected foreground color */
dmenucmd[10] = argv[++i];
#else
2019-10-05 23:56:43 +02:00
else if (!strcmp("-df", argv[i])) /* dmenu font */
dmenucmd[4] = argv[++i];
else if (!strcmp("-dnb", argv[i])) /* dmenu normal background color */
dmenucmd[6] = argv[++i];
else if (!strcmp("-dnf", argv[i])) /* dmenu normal foreground color */
dmenucmd[8] = argv[++i];
else if (!strcmp("-dsb", argv[i])) /* dmenu selected background color */
dmenucmd[10] = argv[++i];
else if (!strcmp("-dsf", argv[i])) /* dmenu selected foreground color */
dmenucmd[12] = argv[++i];
#endif // NODMENU_PATCH
2020-02-11 18:31:11 +01:00
else die(help());
2019-10-05 23:56:43 +02:00
#else
2019-09-05 00:16:39 +02:00
if (argc == 2 && !strcmp("-v", argv[1]))
die("dwm-"VERSION);
else if (argc != 1)
die("usage: dwm [-v]");
#endif // CMDCUSTOMIZE_PATCH
2019-09-05 00:16:39 +02:00
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL)))
die("dwm: cannot open display");
2019-10-22 19:08:00 +02:00
#if SWALLOW_PATCH
if (!(xcon = XGetXCBConnection(dpy)))
die("dwm: cannot get xcb connection\n");
#endif // SWALLOW_PATCH
2019-09-05 00:16:39 +02:00
checkotherwm();
#if XRDB_PATCH && !BAR_VTCOLORS_PATCH
2019-09-15 22:45:22 +02:00
XrmInitialize();
loadxrdb();
#endif // XRDB_PATCH && !BAR_VTCOLORS_PATCH
2020-08-10 10:22:21 +02:00
#if COOL_AUTOSTART_PATCH
autostart_exec();
#endif // COOL_AUTOSTART_PATCH
2019-09-05 00:16:39 +02:00
setup();
#ifdef __OpenBSD__
if (pledge("stdio rpath proc exec", NULL) == -1)
die("pledge");
#endif /* __OpenBSD__ */
scan();
2019-09-06 23:37:30 +02:00
#if AUTOSTART_PATCH
runautostart();
2019-09-06 23:37:30 +02:00
#endif
2019-09-05 00:16:39 +02:00
run();
#if RESTARTSIG_PATCH
if (restart)
execvp(argv[0], argv);
#endif // RESTARTSIG_PATCH
2019-09-05 00:16:39 +02:00
cleanup();
XCloseDisplay(dpy);
return EXIT_SUCCESS;
}