mirror of
https://github.com/mintycube/dwm.git
synced 2024-10-22 12:05:45 +00:00
36cbcf53a2
The natural cycle for dwm-msg is that: - the client registers - the client sends a message - a response is sent back - the client deregisters There is a race condition such that a new client may end up with the same file descriptor as another command that is deregistering, resulting in a message to come through from a file descriptor that is not registered. The handling of this situation is that the IPC patch will log: Got event from unknown fd 7, ptr 0x7, u32 7, u64 7 with events 17 before gracefully stopping (exiting) dwm. The consequence of the error itself seems benign and the proposal here is to allow dwm to keep running despite not being able to process the dwm-msg command successfully.
5377 lines
143 KiB
C
5377 lines
143 KiB
C
/* See LICENSE file for copyright and license details.
|
|
*
|
|
* dynamic window manager is designed like any other X client as well. It is
|
|
* driven through handling X events. In contrast to other X clients, a window
|
|
* manager selects for SubstructureRedirectMask on the root window, to receive
|
|
* events about window (dis-)appearance. Only one X connection at a time is
|
|
* allowed to select for this event mask.
|
|
*
|
|
* The event handlers of dwm are organized in an array which is accessed
|
|
* whenever a new event has been fetched. This allows event dispatching
|
|
* in O(1) time.
|
|
*
|
|
* Each child of the root window is called a client, except windows which have
|
|
* set the override_redirect flag. Clients are organized in a linked client
|
|
* list on each monitor, the focus history is remembered through a stack list
|
|
* on each monitor. Each client contains a bit array to indicate the tags of a
|
|
* client.
|
|
*
|
|
* Keys and tagging rules are organized as arrays and defined in config.h.
|
|
*
|
|
* To understand everything else, start reading main().
|
|
*/
|
|
#include <errno.h>
|
|
#include <locale.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <X11/cursorfont.h>
|
|
#include <X11/keysym.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/Xutil.h>
|
|
#ifdef XINERAMA
|
|
#include <X11/extensions/Xinerama.h>
|
|
#endif /* XINERAMA */
|
|
#include <X11/Xft/Xft.h>
|
|
|
|
#include "patches.h"
|
|
#include "drw.h"
|
|
#include "util.h"
|
|
|
|
#if BAR_FLEXWINTITLE_PATCH
|
|
#ifndef FLEXTILE_DELUXE_LAYOUT
|
|
#define FLEXTILE_DELUXE_LAYOUT 1
|
|
#endif
|
|
#endif
|
|
|
|
#if BAR_PANGO_PATCH
|
|
#include <pango/pango.h>
|
|
#endif // BAR_PANGO_PATCH
|
|
|
|
#if RESTARTSIG_PATCH
|
|
#include <poll.h>
|
|
#endif // RESTARTSIG_PATCH
|
|
|
|
#if XKB_PATCH
|
|
#include <X11/XKBlib.h>
|
|
#endif // XKB_PATCH
|
|
|
|
#if SPAWNCMD_PATCH
|
|
#include <assert.h>
|
|
#include <libgen.h>
|
|
#include <sys/stat.h>
|
|
#define SPAWN_CWD_DELIM " []{}()<>\"':"
|
|
#endif // SPAWNCMD_PATCH
|
|
|
|
/* macros */
|
|
#define Button6 6
|
|
#define Button7 7
|
|
#define Button8 8
|
|
#define Button9 9
|
|
#define NUMTAGS 9
|
|
#define NUMVIEWHIST NUMTAGS
|
|
#define BARRULES 20
|
|
#if TAB_PATCH
|
|
#define MAXTABS 50
|
|
#endif // TAB_PATCH
|
|
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
|
|
#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
|
|
#if BAR_ANYBAR_PATCH
|
|
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->mx+(m)->mw) - MAX((x),(m)->mx)) \
|
|
* MAX(0, MIN((y)+(h),(m)->my+(m)->mh) - MAX((y),(m)->my)))
|
|
#else
|
|
#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
|
|
* MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
|
|
#endif // BAR_ANYBAR_PATCH
|
|
#if ATTACHASIDE_PATCH && STICKY_PATCH
|
|
#define ISVISIBLEONTAG(C, T) ((C->tags & T) || C->issticky)
|
|
#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags])
|
|
#elif ATTACHASIDE_PATCH
|
|
#define ISVISIBLEONTAG(C, T) ((C->tags & T))
|
|
#define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags])
|
|
#elif STICKY_PATCH
|
|
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky)
|
|
#else
|
|
#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]))
|
|
#endif // ATTACHASIDE_PATCH
|
|
#define LENGTH(X) (sizeof X / sizeof X[0])
|
|
#define MOUSEMASK (BUTTONMASK|PointerMotionMask)
|
|
#define WIDTH(X) ((X)->w + 2 * (X)->bw)
|
|
#define HEIGHT(X) ((X)->h + 2 * (X)->bw)
|
|
#define WTYPE "_NET_WM_WINDOW_TYPE_"
|
|
#if SCRATCHPADS_PATCH && !RENAMED_SCRATCHPADS_PATCH
|
|
#define TOTALTAGS (NUMTAGS + LENGTH(scratchpads))
|
|
#define TAGMASK ((1 << TOTALTAGS) - 1)
|
|
#define SPTAG(i) ((1 << NUMTAGS) << (i))
|
|
#define SPTAGMASK (((1 << LENGTH(scratchpads))-1) << NUMTAGS)
|
|
#else
|
|
#define TAGMASK ((1 << NUMTAGS) - 1)
|
|
#endif // SCRATCHPADS_PATCH
|
|
#define TEXTWM(X) (drw_fontset_getwidth(drw, (X), True) + lrpad)
|
|
#define TEXTW(X) (drw_fontset_getwidth(drw, (X), False) + lrpad)
|
|
#define HIDDEN(C) ((getstate(C->win) == IconicState))
|
|
|
|
/* enums */
|
|
enum {
|
|
#if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH
|
|
CurResizeBR,
|
|
CurResizeBL,
|
|
CurResizeTR,
|
|
CurResizeTL,
|
|
#endif // RESIZEPOINT_PATCH | RESIZECORNERS_PATCH
|
|
#if DRAGMFACT_PATCH
|
|
CurResizeHorzArrow,
|
|
CurResizeVertArrow,
|
|
#endif // DRAGMFACT_PATCH
|
|
#if DRAGCFACT_PATCH
|
|
CurIronCross,
|
|
#endif // DRAGCFACT_PATCH
|
|
CurNormal,
|
|
CurResize,
|
|
CurMove,
|
|
CurLast
|
|
}; /* cursor */
|
|
|
|
enum {
|
|
SchemeNorm,
|
|
SchemeSel,
|
|
SchemeTitleNorm,
|
|
SchemeTitleSel,
|
|
SchemeTagsNorm,
|
|
SchemeTagsSel,
|
|
SchemeHidNorm,
|
|
SchemeHidSel,
|
|
SchemeUrg,
|
|
#if RENAMED_SCRATCHPADS_PATCH
|
|
SchemeScratchSel,
|
|
SchemeScratchNorm,
|
|
#endif // RENAMED_SCRATCHPADS_PATCH
|
|
#if BAR_FLEXWINTITLE_PATCH
|
|
SchemeFlexActTTB,
|
|
SchemeFlexActLTR,
|
|
SchemeFlexActMONO,
|
|
SchemeFlexActGRID,
|
|
SchemeFlexActGRD1,
|
|
SchemeFlexActGRD2,
|
|
SchemeFlexActGRDM,
|
|
SchemeFlexActHGRD,
|
|
SchemeFlexActDWDL,
|
|
SchemeFlexActSPRL,
|
|
SchemeFlexInaTTB,
|
|
SchemeFlexInaLTR,
|
|
SchemeFlexInaMONO,
|
|
SchemeFlexInaGRID,
|
|
SchemeFlexInaGRD1,
|
|
SchemeFlexInaGRD2,
|
|
SchemeFlexInaGRDM,
|
|
SchemeFlexInaHGRD,
|
|
SchemeFlexInaDWDL,
|
|
SchemeFlexInaSPRL,
|
|
SchemeFlexSelTTB,
|
|
SchemeFlexSelLTR,
|
|
SchemeFlexSelMONO,
|
|
SchemeFlexSelGRID,
|
|
SchemeFlexSelGRD1,
|
|
SchemeFlexSelGRD2,
|
|
SchemeFlexSelGRDM,
|
|
SchemeFlexSelHGRD,
|
|
SchemeFlexSelDWDL,
|
|
SchemeFlexSelSPRL,
|
|
SchemeFlexActFloat,
|
|
SchemeFlexInaFloat,
|
|
SchemeFlexSelFloat,
|
|
#endif // BAR_FLEXWINTITLE_PATCH
|
|
}; /* color schemes */
|
|
|
|
enum {
|
|
NetSupported, NetWMName, NetWMState, NetWMCheck,
|
|
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
|
|
#if BAR_WINICON_PATCH
|
|
NetWMIcon,
|
|
#endif // BAR_WINICON_PATCH
|
|
#if BAR_SYSTRAY_PATCH
|
|
NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation,
|
|
NetSystemTrayVisual, NetWMWindowTypeDock, NetSystemTrayOrientationHorz,
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
#if BAR_EWMHTAGS_PATCH
|
|
NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop,
|
|
#endif // BAR_EWMHTAGS_PATCH
|
|
NetClientList,
|
|
#if NET_CLIENT_LIST_STACKING_PATCH
|
|
NetClientListStacking,
|
|
#endif // NET_CLIENT_LIST_STACKING_PATCH
|
|
NetLast
|
|
}; /* EWMH atoms */
|
|
|
|
enum {
|
|
WMProtocols,
|
|
WMDelete,
|
|
WMState,
|
|
WMTakeFocus,
|
|
#if WINDOWROLERULE_PATCH
|
|
WMWindowRole,
|
|
#endif // WINDOWROLERULE_PATCH
|
|
WMLast
|
|
}; /* default atoms */
|
|
|
|
#if SEAMLESS_RESTART_PATCH
|
|
enum {
|
|
ClientFields,
|
|
ClientTags,
|
|
ClientLast
|
|
}; /* dwm client atoms */
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
|
|
enum {
|
|
#if BAR_STATUSBUTTON_PATCH
|
|
ClkButton,
|
|
#endif // BAR_STATUSBUTTON_PATCH
|
|
#if TAB_PATCH
|
|
ClkTabBar,
|
|
#endif // TAB_PATCH
|
|
ClkTagBar,
|
|
ClkLtSymbol,
|
|
ClkStatusText,
|
|
ClkWinTitle,
|
|
ClkClientWin,
|
|
ClkRootWin,
|
|
#if XKB_PATCH
|
|
ClkXKB,
|
|
#endif // XKB_PATCH
|
|
ClkLast
|
|
}; /* clicks */
|
|
|
|
enum {
|
|
BAR_ALIGN_LEFT,
|
|
BAR_ALIGN_CENTER,
|
|
BAR_ALIGN_RIGHT,
|
|
BAR_ALIGN_LEFT_LEFT,
|
|
BAR_ALIGN_LEFT_RIGHT,
|
|
BAR_ALIGN_LEFT_CENTER,
|
|
BAR_ALIGN_NONE,
|
|
BAR_ALIGN_RIGHT_LEFT,
|
|
BAR_ALIGN_RIGHT_RIGHT,
|
|
BAR_ALIGN_RIGHT_CENTER,
|
|
BAR_ALIGN_LAST
|
|
}; /* bar alignment */
|
|
|
|
#if IPC_PATCH
|
|
typedef struct TagState TagState;
|
|
struct TagState {
|
|
int selected;
|
|
int occupied;
|
|
int urgent;
|
|
};
|
|
|
|
typedef struct ClientState ClientState;
|
|
struct ClientState {
|
|
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
|
|
};
|
|
#endif // IPC_PATCH
|
|
|
|
typedef union {
|
|
#if IPC_PATCH
|
|
long i;
|
|
unsigned long ui;
|
|
#else
|
|
int i;
|
|
unsigned int ui;
|
|
#endif // IPC_PATCH
|
|
float f;
|
|
const void *v;
|
|
} Arg;
|
|
|
|
typedef struct Monitor Monitor;
|
|
typedef struct Bar Bar;
|
|
struct Bar {
|
|
Window win;
|
|
Monitor *mon;
|
|
Bar *next;
|
|
int idx;
|
|
int showbar;
|
|
int topbar;
|
|
int external;
|
|
int borderpx;
|
|
int borderscheme;
|
|
int bx, by, bw, bh; /* bar geometry */
|
|
int w[BARRULES]; // width, array length == barrules, then use r index for lookup purposes
|
|
int x[BARRULES]; // x position, array length == ^
|
|
};
|
|
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
int h;
|
|
int w;
|
|
} BarArg;
|
|
|
|
typedef struct {
|
|
int monitor;
|
|
int bar;
|
|
int alignment; // see bar alignment enum
|
|
int (*widthfunc)(Bar *bar, BarArg *a);
|
|
int (*drawfunc)(Bar *bar, BarArg *a);
|
|
int (*clickfunc)(Bar *bar, Arg *arg, BarArg *a);
|
|
int (*hoverfunc)(Bar *bar, BarArg *a, XMotionEvent *ev);
|
|
char *name; // for debugging
|
|
int x, w; // position, width for internal use
|
|
} BarRule;
|
|
|
|
typedef struct {
|
|
unsigned int click;
|
|
unsigned int mask;
|
|
unsigned int button;
|
|
void (*func)(const Arg *arg);
|
|
const Arg arg;
|
|
} Button;
|
|
|
|
#if XKB_PATCH
|
|
typedef struct XkbInfo XkbInfo;
|
|
struct XkbInfo {
|
|
XkbInfo *next;
|
|
XkbInfo *prev;
|
|
int group;
|
|
Window w;
|
|
};
|
|
#endif // XKB_PATCH
|
|
|
|
typedef struct Client Client;
|
|
struct Client {
|
|
char name[256];
|
|
float mina, maxa;
|
|
#if CFACTS_PATCH
|
|
float cfact;
|
|
#endif // CFACTS_PATCH
|
|
int x, y, w, h;
|
|
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
|
|
int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */
|
|
#endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH
|
|
#if SEAMLESS_RESTART_PATCH
|
|
unsigned int idx;
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
int oldx, oldy, oldw, oldh;
|
|
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
|
|
int bw, oldbw;
|
|
unsigned int tags;
|
|
#if SWITCHTAG_PATCH
|
|
unsigned int switchtag;
|
|
#endif // SWITCHTAG_PATCH
|
|
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
|
|
#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH
|
|
int fakefullscreen;
|
|
#endif // FAKEFULLSCREEN_CLIENT_PATCH
|
|
#if EXRESIZE_PATCH
|
|
unsigned char expandmask;
|
|
int expandx1, expandy1, expandx2, expandy2;
|
|
#if !MAXIMIZE_PATCH
|
|
int wasfloating;
|
|
#endif // MAXIMIZE_PATCH
|
|
#endif // EXRESIZE_PATCH
|
|
#if MAXIMIZE_PATCH
|
|
int ismax, wasfloating;
|
|
#endif // MAXIMIZE_PATCH
|
|
#if AUTORESIZE_PATCH
|
|
int needresize;
|
|
#endif // AUTORESIZE_PATCH
|
|
#if CENTER_PATCH
|
|
int iscentered;
|
|
#endif // CENTER_PATCH
|
|
#if ISPERMANENT_PATCH
|
|
int ispermanent;
|
|
#endif // ISPERMANENT_PATCH
|
|
#if PLACEMOUSE_PATCH
|
|
int beingmoved;
|
|
#endif // PLACEMOUSE_PATCH
|
|
#if SIZEHINTS_ISFREESIZE_PATCH
|
|
int isfreesize;
|
|
#endif // SIZEHINTS_ISFREESIZE_PATCH
|
|
#if SWALLOW_PATCH
|
|
int isterminal, noswallow;
|
|
pid_t pid;
|
|
#endif // SWALLOW_PATCH
|
|
#if STEAM_PATCH
|
|
int issteam;
|
|
#endif // STEAM_PATCH
|
|
#if STICKY_PATCH
|
|
int issticky;
|
|
#endif // STICKY_PATCH
|
|
Client *next;
|
|
Client *snext;
|
|
#if SWALLOW_PATCH
|
|
Client *swallowing;
|
|
#endif // SWALLOW_PATCH
|
|
Monitor *mon;
|
|
Window win;
|
|
#if IPC_PATCH
|
|
ClientState prevstate;
|
|
#endif // IPC_PATCH
|
|
#if RENAMED_SCRATCHPADS_PATCH
|
|
char scratchkey;
|
|
#endif // RENAMED_SCRATCHPADS_PATCH
|
|
#if XKB_PATCH
|
|
XkbInfo *xkb;
|
|
#endif // XKB_PATCH
|
|
#if BAR_WINICON_PATCH
|
|
unsigned int icw, ich;
|
|
Picture icon;
|
|
#endif // BAR_WINICON_PATCH
|
|
};
|
|
|
|
typedef struct {
|
|
unsigned int mod;
|
|
KeySym keysym;
|
|
void (*func)(const Arg *);
|
|
const Arg arg;
|
|
} Key;
|
|
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
typedef struct {
|
|
int nmaster;
|
|
int nstack;
|
|
int layout;
|
|
int masteraxis; // master stack area
|
|
int stack1axis; // primary stack area
|
|
int stack2axis; // secondary stack area, e.g. centered master
|
|
void (*symbolfunc)(Monitor *, unsigned int);
|
|
} LayoutPreset;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
|
|
typedef struct {
|
|
const char *symbol;
|
|
void (*arrange)(Monitor *);
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
LayoutPreset preset;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
} Layout;
|
|
|
|
#if INSETS_PATCH
|
|
typedef struct {
|
|
int x;
|
|
int y;
|
|
int w;
|
|
int h;
|
|
} Inset;
|
|
#endif // INSETS_PATCH
|
|
|
|
#if PERTAG_PATCH
|
|
typedef struct Pertag Pertag;
|
|
#endif // PERTAG_PATCH
|
|
struct Monitor {
|
|
char ltsymbol[16];
|
|
float mfact;
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
int ltaxis[4];
|
|
int nstack;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
int nmaster;
|
|
int num;
|
|
int mx, my, mw, mh; /* screen size */
|
|
int wx, wy, ww, wh; /* window area */
|
|
#if TAB_PATCH
|
|
int ty; /* tab bar geometry */
|
|
#endif // TAB_PATCH
|
|
#if VANITYGAPS_PATCH
|
|
int gappih; /* horizontal gap between windows */
|
|
int gappiv; /* vertical gap between windows */
|
|
int gappoh; /* horizontal outer gaps */
|
|
int gappov; /* vertical outer gaps */
|
|
#if PERMON_VANITYGAPS_PATCH
|
|
int enablegaps; /* whether gaps are enabled */
|
|
#endif // PERMON_VANITYGAPS_PATCH
|
|
#endif // VANITYGAPS_PATCH
|
|
#if SETBORDERPX_PATCH
|
|
int borderpx;
|
|
#endif // SETBORDERPX_PATCH
|
|
unsigned int seltags;
|
|
unsigned int sellt;
|
|
#if VIEW_HISTORY_PATCH
|
|
unsigned int tagset[NUMVIEWHIST];
|
|
#else
|
|
unsigned int tagset[2];
|
|
#endif // VIEW_HISTORY_PATCH
|
|
int showbar;
|
|
#if TAB_PATCH
|
|
int showtab;
|
|
int toptab;
|
|
Window tabwin;
|
|
int ntabs;
|
|
int tab_widths[MAXTABS];
|
|
#endif // TAB_PATCH
|
|
Client *clients;
|
|
Client *sel;
|
|
Client *stack;
|
|
#if FOCUSMASTER_RETURN_PATCH
|
|
Client *tagmarked[32];
|
|
#endif // FOCUSMASTER_RETURN_PATCH
|
|
Monitor *next;
|
|
Bar *bar;
|
|
const Layout *lt[2];
|
|
#if BAR_ALTERNATIVE_TAGS_PATCH
|
|
unsigned int alttag;
|
|
#endif // BAR_ALTERNATIVE_TAGS_PATCH
|
|
#if PERTAG_PATCH
|
|
Pertag *pertag;
|
|
#endif // PERTAG_PATCH
|
|
#if INSETS_PATCH
|
|
Inset inset;
|
|
#endif // INSETS_PATCH
|
|
#if BAR_TAGLABELS_PATCH
|
|
char taglabel[NUMTAGS][64];
|
|
#endif // BAR_TAGLABELS_PATCH
|
|
#if IPC_PATCH
|
|
char lastltsymbol[16];
|
|
TagState tagstate;
|
|
Client *lastsel;
|
|
const Layout *lastlt;
|
|
#endif // IPC_PATCH
|
|
#if BAR_TAGPREVIEW_PATCH
|
|
Window tagwin;
|
|
int previewshow;
|
|
Pixmap tagmap[NUMTAGS];
|
|
#endif // BAR_TAGPREVIEW_PATCH
|
|
};
|
|
|
|
typedef struct {
|
|
const char *class;
|
|
#if WINDOWROLERULE_PATCH
|
|
const char *role;
|
|
#endif // WINDOWROLERULE_PATCH
|
|
const char *instance;
|
|
const char *title;
|
|
const char *wintype;
|
|
unsigned int tags;
|
|
#if SWITCHTAG_PATCH
|
|
int switchtag;
|
|
#endif // SWITCHTAG_PATCH
|
|
#if CENTER_PATCH
|
|
int iscentered;
|
|
#endif // CENTER_PATCH
|
|
int isfloating;
|
|
#if SELECTIVEFAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
|
|
int isfakefullscreen;
|
|
#endif // SELECTIVEFAKEFULLSCREEN_PATCH
|
|
#if SIZEHINTS_ISFREESIZE_PATCH
|
|
int isfreesize;
|
|
#endif // SIZEHINTS_ISFREESIZE_PATCH
|
|
#if ISPERMANENT_PATCH
|
|
int ispermanent;
|
|
#endif // ISPERMANENT_PATCH
|
|
#if SWALLOW_PATCH
|
|
int isterminal;
|
|
int noswallow;
|
|
#endif // SWALLOW_PATCH
|
|
#if FLOATPOS_PATCH
|
|
const char *floatpos;
|
|
#endif // FLOATPOS_PATCH
|
|
int monitor;
|
|
#if RENAMED_SCRATCHPADS_PATCH
|
|
const char scratchkey;
|
|
#endif // RENAMED_SCRATCHPADS_PATCH
|
|
#if UNMANAGED_PATCH
|
|
int unmanaged;
|
|
#endif // UNMANAGED_PATCH
|
|
#if XKB_PATCH
|
|
int xkb_layout;
|
|
#endif // XKB_PATCH
|
|
} Rule;
|
|
|
|
#if XKB_PATCH
|
|
#define RULE(...) { .monitor = -1, .xkb_layout = -1, __VA_ARGS__ },
|
|
#else
|
|
#define RULE(...) { .monitor = -1, __VA_ARGS__ },
|
|
#endif // XKB_PATCH
|
|
|
|
/* Cross patch compatibility rule macro helper macros */
|
|
#define FLOATING , .isfloating = 1
|
|
#if CENTER_PATCH
|
|
#define CENTERED , .iscentered = 1
|
|
#else
|
|
#define CENTERED
|
|
#endif // CENTER_PATCH
|
|
#if ISPERMANENT_PATCH
|
|
#define PERMANENT , .ispermanent = 1
|
|
#else
|
|
#define PERMANENT
|
|
#endif // ISPERMANENT_PATCH
|
|
#if SELECTIVEFAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
|
|
#define FAKEFULLSCREEN , .isfakefullscreen = 1
|
|
#else
|
|
#define FAKEFULLSCREEN
|
|
#endif // SELECTIVEFAKEFULLSCREEN_PATCH
|
|
#if SWALLOW_PATCH
|
|
#define NOSWALLOW , .noswallow = 1
|
|
#define TERMINAL , .isterminal = 1
|
|
#else
|
|
#define NOSWALLOW
|
|
#define TERMINAL
|
|
#endif // SWALLOW_PATCH
|
|
#if SWITCHTAG_PATCH
|
|
#define SWITCHTAG , .switchtag = 1
|
|
#else
|
|
#define SWITCHTAG
|
|
#endif // SWITCHTAG_PATCH
|
|
|
|
#if MONITOR_RULES_PATCH
|
|
typedef struct {
|
|
int monitor;
|
|
#if PERTAG_PATCH
|
|
int tag;
|
|
#endif // PERTAG_PATCH
|
|
int layout;
|
|
float mfact;
|
|
int nmaster;
|
|
int showbar;
|
|
int topbar;
|
|
} MonitorRule;
|
|
#endif // MONITOR_RULES_PATCH
|
|
|
|
/* function declarations */
|
|
static void applyrules(Client *c);
|
|
static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
|
|
static void arrange(Monitor *m);
|
|
static void arrangemon(Monitor *m);
|
|
static void attach(Client *c);
|
|
static void attachstack(Client *c);
|
|
static void buttonpress(XEvent *e);
|
|
static void checkotherwm(void);
|
|
static void cleanup(void);
|
|
static void cleanupmon(Monitor *mon);
|
|
static void clientmessage(XEvent *e);
|
|
static void configure(Client *c);
|
|
static void configurenotify(XEvent *e);
|
|
static void configurerequest(XEvent *e);
|
|
static Monitor *createmon(void);
|
|
static void destroynotify(XEvent *e);
|
|
static void detach(Client *c);
|
|
static void detachstack(Client *c);
|
|
static Monitor *dirtomon(int dir);
|
|
static void drawbar(Monitor *m);
|
|
static void drawbars(void);
|
|
static void drawbarwin(Bar *bar);
|
|
#if !FOCUSONCLICK_PATCH
|
|
static void enternotify(XEvent *e);
|
|
#endif // FOCUSONCLICK_PATCH
|
|
static void expose(XEvent *e);
|
|
static void focus(Client *c);
|
|
static void focusin(XEvent *e);
|
|
static void focusmon(const Arg *arg);
|
|
#if !STACKER_PATCH
|
|
static void focusstack(const Arg *arg);
|
|
#endif // STACKER_PATCH
|
|
static Atom getatomprop(Client *c, Atom prop, Atom req);
|
|
static int getrootptr(int *x, int *y);
|
|
static long getstate(Window w);
|
|
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
|
|
static void grabbuttons(Client *c, int focused);
|
|
#if KEYMODES_PATCH
|
|
static void grabdefkeys(void);
|
|
#else
|
|
static void grabkeys(void);
|
|
#endif // KEYMODES_PATCH
|
|
static void incnmaster(const Arg *arg);
|
|
#if KEYMODES_PATCH
|
|
static void keydefpress(XEvent *e);
|
|
#else
|
|
static void keypress(XEvent *e);
|
|
#endif // KEYMODES_PATCH
|
|
static void killclient(const Arg *arg);
|
|
static void manage(Window w, XWindowAttributes *wa);
|
|
static void mappingnotify(XEvent *e);
|
|
static void maprequest(XEvent *e);
|
|
static void motionnotify(XEvent *e);
|
|
static void movemouse(const Arg *arg);
|
|
static Client *nexttiled(Client *c);
|
|
#if NOBORDER_PATCH
|
|
static int noborder(Client *c);
|
|
#endif // NOBORDER_PATCH
|
|
#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH
|
|
static void pop(Client *c);
|
|
#endif // !ZOOMSWAP_PATCH / TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH
|
|
static void propertynotify(XEvent *e);
|
|
static void quit(const Arg *arg);
|
|
static Monitor *recttomon(int x, int y, int w, int h);
|
|
static void resize(Client *c, int x, int y, int w, int h, int interact);
|
|
static void resizeclient(Client *c, int x, int y, int w, int h);
|
|
static void resizemouse(const Arg *arg);
|
|
static void restack(Monitor *m);
|
|
static void run(void);
|
|
static void scan(void);
|
|
#if BAR_SYSTRAY_PATCH
|
|
static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
|
|
#else
|
|
static int sendevent(Client *c, Atom proto);
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
static void sendmon(Client *c, Monitor *m);
|
|
static void setclientstate(Client *c, long state);
|
|
static void setfocus(Client *c);
|
|
static void setfullscreen(Client *c, int fullscreen);
|
|
static void setlayout(const Arg *arg);
|
|
static void setmfact(const Arg *arg);
|
|
static void setup(void);
|
|
static void seturgent(Client *c, int urg);
|
|
#if COOL_AUTOSTART_PATCH
|
|
static void sigchld(int unused);
|
|
#endif // COOL_AUTOSTART_PATCH
|
|
static void showhide(Client *c);
|
|
static void spawn(const Arg *arg);
|
|
#if RIODRAW_PATCH
|
|
static pid_t spawncmd(const Arg *arg);
|
|
#endif // RIODRAW_PATCH
|
|
static void tag(const Arg *arg);
|
|
static void tagmon(const Arg *arg);
|
|
static void togglebar(const Arg *arg);
|
|
static void togglefloating(const Arg *arg);
|
|
static void toggletag(const Arg *arg);
|
|
static void toggleview(const Arg *arg);
|
|
static void unfocus(Client *c, int setfocus, Client *nextfocus);
|
|
static void unmanage(Client *c, int destroyed);
|
|
static void unmapnotify(XEvent *e);
|
|
static void updatebarpos(Monitor *m);
|
|
static void updatebars(void);
|
|
static void updateclientlist(void);
|
|
static int updategeom(void);
|
|
static void updatenumlockmask(void);
|
|
static void updatesizehints(Client *c);
|
|
static void updatestatus(void);
|
|
static void updatetitle(Client *c);
|
|
static void updatewmhints(Client *c);
|
|
static void view(const Arg *arg);
|
|
static Client *wintoclient(Window w);
|
|
static Monitor *wintomon(Window w);
|
|
static int xerror(Display *dpy, XErrorEvent *ee);
|
|
static int xerrordummy(Display *dpy, XErrorEvent *ee);
|
|
static int xerrorstart(Display *dpy, XErrorEvent *ee);
|
|
static void zoom(const Arg *arg);
|
|
|
|
/* bar functions */
|
|
|
|
#include "patch/include.h"
|
|
|
|
/* variables */
|
|
static const char broken[] = "broken";
|
|
#if BAR_PANGO_PATCH || BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH
|
|
static char stext[1024];
|
|
#else
|
|
static char stext[512];
|
|
#endif // BAR_PANGO_PATCH | BAR_STATUS2D_PATCH
|
|
#if BAR_EXTRASTATUS_PATCH || BAR_STATUSCMD_PATCH
|
|
#if BAR_STATUS2D_PATCH
|
|
static char rawstext[1024];
|
|
#else
|
|
static char rawstext[512];
|
|
#endif // BAR_STATUS2D_PATCH
|
|
#endif // BAR_EXTRASTATUS_PATCH | BAR_STATUSCMD_PATCH
|
|
#if BAR_EXTRASTATUS_PATCH
|
|
#if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH
|
|
static char estext[1024];
|
|
#else
|
|
static char estext[512];
|
|
#endif // BAR_STATUS2D_PATCH
|
|
#if BAR_STATUSCMD_PATCH
|
|
static char rawestext[1024];
|
|
#endif // BAR_STATUS2D_PATCH | BAR_STATUSCMD_PATCH
|
|
#endif // BAR_EXTRASTATUS_PATCH
|
|
|
|
#if XKB_PATCH
|
|
static int xkbEventType = 0;
|
|
#endif // XKB_PATCH
|
|
static int screen;
|
|
static int sw, sh; /* X display screen geometry width, height */
|
|
static int bh; /* bar geometry */
|
|
#if UNMANAGED_PATCH
|
|
static int unmanaged = 0; /* whether the window manager should manage the new window or not */
|
|
#endif // UNMANAGED_PATCH
|
|
static int lrpad; /* sum of left and right padding for text */
|
|
/* Some clients (e.g. alacritty) helpfully send configure requests with a new size or position
|
|
* when they detect that they have been moved to another monitor. This can cause visual glitches
|
|
* when moving (or resizing) client windows from one monitor to another. This variable is used
|
|
* internally to ignore such configure requests while movemouse or resizemouse are being used. */
|
|
static int ignoreconfigurerequests = 0;
|
|
#if WARP_PATCH
|
|
static int force_warp = 0; // force warp in some situations, e.g. killclient
|
|
static int ignore_warp = 0; // force skip warp in some situations, e.g. dragmfact, dragcfact
|
|
#endif // WARP_PATCH
|
|
static int (*xerrorxlib)(Display *, XErrorEvent *);
|
|
static unsigned int numlockmask = 0;
|
|
#if RIODRAW_PATCH
|
|
static int riodimensions[4] = { -1, -1, -1, -1 };
|
|
static pid_t riopid = 0;
|
|
#endif // RIODRAW_PATCH
|
|
static void (*handler[LASTEvent]) (XEvent *) = {
|
|
[ButtonPress] = buttonpress,
|
|
#if COMBO_PATCH || BAR_HOLDBAR_PATCH
|
|
[ButtonRelease] = keyrelease,
|
|
#endif // COMBO_PATCH / BAR_HOLDBAR_PATCH
|
|
[ClientMessage] = clientmessage,
|
|
[ConfigureRequest] = configurerequest,
|
|
[ConfigureNotify] = configurenotify,
|
|
[DestroyNotify] = destroynotify,
|
|
#if !FOCUSONCLICK_PATCH
|
|
[EnterNotify] = enternotify,
|
|
#endif // FOCUSONCLICK_PATCH
|
|
[Expose] = expose,
|
|
[FocusIn] = focusin,
|
|
[KeyPress] = keypress,
|
|
#if COMBO_PATCH || BAR_HOLDBAR_PATCH
|
|
[KeyRelease] = keyrelease,
|
|
#endif // COMBO_PATCH / BAR_HOLDBAR_PATCH
|
|
[MappingNotify] = mappingnotify,
|
|
[MapRequest] = maprequest,
|
|
[MotionNotify] = motionnotify,
|
|
[PropertyNotify] = propertynotify,
|
|
#if BAR_SYSTRAY_PATCH
|
|
[ResizeRequest] = resizerequest,
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
[UnmapNotify] = unmapnotify
|
|
};
|
|
static Atom wmatom[WMLast], netatom[NetLast];
|
|
#if BAR_SYSTRAY_PATCH
|
|
static Atom xatom[XLast];
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
#if SEAMLESS_RESTART_PATCH
|
|
static Atom clientatom[ClientLast];
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
#if ON_EMPTY_KEYS_PATCH
|
|
static int isempty = 0;
|
|
#endif // ON_EMPTY_KEYS_PATCH
|
|
#if RESTARTSIG_PATCH
|
|
static volatile sig_atomic_t running = 1;
|
|
#else
|
|
static int running = 1;
|
|
#endif // RESTARTSIG_PATCH
|
|
static Cur *cursor[CurLast];
|
|
static Clr **scheme;
|
|
static Display *dpy;
|
|
static Drw *drw;
|
|
static Monitor *mons, *selmon;
|
|
static Window root, wmcheckwin;
|
|
|
|
/* configuration, allows nested code to access above variables */
|
|
#include "config.h"
|
|
|
|
#include "patch/include.c"
|
|
|
|
/* compile-time check if all tags fit into an unsigned int bit array. */
|
|
#if SCRATCHPAD_ALT_1_PATCH
|
|
struct NumTags { char limitexceeded[NUMTAGS > 30 ? -1 : 1]; };
|
|
#else
|
|
struct NumTags { char limitexceeded[NUMTAGS > 31 ? -1 : 1]; };
|
|
#endif // SCRATCHPAD_ALT_1_PATCH
|
|
|
|
/* function implementations */
|
|
void
|
|
applyrules(Client *c)
|
|
{
|
|
const char *class, *instance;
|
|
Atom wintype;
|
|
#if WINDOWROLERULE_PATCH
|
|
char role[64];
|
|
#endif // WINDOWROLERULE_PATCH
|
|
unsigned int i;
|
|
#if SWITCHTAG_PATCH
|
|
unsigned int newtagset;
|
|
#endif // SWITCHTAG_PATCH
|
|
const Rule *r;
|
|
Monitor *m;
|
|
XClassHint ch = { NULL, NULL };
|
|
|
|
/* rule matching */
|
|
#if SWALLOW_PATCH
|
|
c->noswallow = -1;
|
|
#endif // SWALLOW_PATCH
|
|
#if SIZEHINTS_ISFREESIZE_PATCH
|
|
c->isfreesize = 1;
|
|
#endif // SIZEHINTS_ISFREESIZE_PATCH
|
|
c->isfloating = 0;
|
|
c->tags = 0;
|
|
#if RENAMED_SCRATCHPADS_PATCH
|
|
c->scratchkey = 0;
|
|
#endif // RENAMED_SCRATCHPADS_PATCH
|
|
XGetClassHint(dpy, c->win, &ch);
|
|
class = ch.res_class ? ch.res_class : broken;
|
|
instance = ch.res_name ? ch.res_name : broken;
|
|
wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM);
|
|
#if WINDOWROLERULE_PATCH
|
|
gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role));
|
|
#endif // WINDOWROLERULE_PATCH
|
|
|
|
#if STEAM_PATCH
|
|
if (strstr(class, "Steam") || strstr(class, "steam_app_"))
|
|
c->issteam = 1;
|
|
#endif // STEAM_PATCH
|
|
|
|
for (i = 0; i < LENGTH(rules); i++) {
|
|
r = &rules[i];
|
|
if ((!r->title || strstr(c->name, r->title))
|
|
&& (!r->class || strstr(class, r->class))
|
|
#if WINDOWROLERULE_PATCH
|
|
&& (!r->role || strstr(role, r->role))
|
|
#endif // WINDOWROLERULE_PATCH
|
|
&& (!r->instance || strstr(instance, r->instance))
|
|
&& (!r->wintype || wintype == XInternAtom(dpy, r->wintype, False)))
|
|
{
|
|
#if CENTER_PATCH
|
|
c->iscentered = r->iscentered;
|
|
#endif // CENTER_PATCH
|
|
#if ISPERMANENT_PATCH
|
|
c->ispermanent = r->ispermanent;
|
|
#endif // ISPERMANENT_PATCH
|
|
#if SELECTIVEFAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
|
|
c->fakefullscreen = r->isfakefullscreen;
|
|
#endif // SELECTIVEFAKEFULLSCREEN_PATCH
|
|
#if SWALLOW_PATCH
|
|
c->isterminal = r->isterminal;
|
|
c->noswallow = r->noswallow;
|
|
#endif // SWALLOW_PATCH
|
|
#if SIZEHINTS_ISFREESIZE_PATCH
|
|
c->isfreesize = r->isfreesize;
|
|
#endif // SIZEHINTS_ISFREESIZE_PATCH
|
|
c->isfloating = r->isfloating;
|
|
c->tags |= r->tags;
|
|
#if RENAMED_SCRATCHPADS_PATCH
|
|
c->scratchkey = r->scratchkey;
|
|
#elif SCRATCHPADS_PATCH
|
|
if ((r->tags & SPTAGMASK) && r->isfloating) {
|
|
c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
|
|
c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
|
|
}
|
|
#endif // SCRATCHPADS_PATCH
|
|
#if UNMANAGED_PATCH
|
|
unmanaged = r->unmanaged;
|
|
#endif // UNMANAGED_PATCH
|
|
for (m = mons; m && m->num != r->monitor; m = m->next);
|
|
if (m)
|
|
c->mon = m;
|
|
#if FLOATPOS_PATCH
|
|
if (c->isfloating && r->floatpos) {
|
|
#if CENTER_PATCH
|
|
c->iscentered = 0;
|
|
#endif // CENTER_PATCH
|
|
setfloatpos(c, r->floatpos);
|
|
}
|
|
#endif // FLOATPOS_PATCH
|
|
|
|
#if SWITCHTAG_PATCH
|
|
#if SWALLOW_PATCH
|
|
if (r->switchtag && (
|
|
c->noswallow > 0 ||
|
|
!termforwin(c) ||
|
|
!(c->isfloating && swallowfloating && c->noswallow < 0)))
|
|
#else
|
|
if (r->switchtag)
|
|
#endif // SWALLOW_PATCH
|
|
{
|
|
unfocus(selmon->sel, 1, NULL);
|
|
selmon = c->mon;
|
|
if (r->switchtag == 2 || r->switchtag == 4)
|
|
newtagset = c->mon->tagset[c->mon->seltags] ^ c->tags;
|
|
else
|
|
newtagset = c->tags;
|
|
|
|
/* Switch to the client's tag, but only if that tag is not already shown */
|
|
if (newtagset && !(c->tags & c->mon->tagset[c->mon->seltags])) {
|
|
if (r->switchtag == 3 || r->switchtag == 4)
|
|
c->switchtag = c->mon->tagset[c->mon->seltags];
|
|
if (r->switchtag == 1 || r->switchtag == 3) {
|
|
view(&((Arg) { .ui = newtagset }));
|
|
} else {
|
|
#if TAGSYNC_PATCH
|
|
for (m = mons; m; m = m->next)
|
|
m->tagset[m->seltags] = newtagset;
|
|
arrange(NULL);
|
|
#else
|
|
c->mon->tagset[c->mon->seltags] = newtagset;
|
|
arrange(c->mon);
|
|
#endif // TAGSYNC_PATCH
|
|
}
|
|
}
|
|
}
|
|
#endif // SWITCHTAG_PATCH
|
|
#if XKB_PATCH
|
|
if (r->xkb_layout > -1)
|
|
c->xkb->group = r->xkb_layout;
|
|
#endif // XKB_PATCH
|
|
#if ONLY_ONE_RULE_MATCH_PATCH
|
|
break;
|
|
#endif // ONLY_ONE_RULE_MATCH_PATCH
|
|
}
|
|
}
|
|
if (ch.res_class)
|
|
XFree(ch.res_class);
|
|
if (ch.res_name)
|
|
XFree(ch.res_name);
|
|
#if EMPTYVIEW_PATCH
|
|
if (c->tags & TAGMASK) c->tags = c->tags & TAGMASK;
|
|
#if SCRATCHPADS_PATCH && !RENAMED_SCRATCHPADS_PATCH
|
|
else if (c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags] & ~SPTAGMASK;
|
|
#elif SCRATCHPAD_ALT_1_PATCH
|
|
else if (c->tags != SCRATCHPAD_MASK && c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags];
|
|
#else
|
|
else if (c->mon->tagset[c->mon->seltags]) c->tags = c->mon->tagset[c->mon->seltags];
|
|
#endif // SCRATCHPADS_PATCH
|
|
else c->tags = 1;
|
|
#elif SCRATCHPADS_PATCH && !RENAMED_SCRATCHPADS_PATCH
|
|
c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : (c->mon->tagset[c->mon->seltags] & ~SPTAGMASK);
|
|
#elif SCRATCHPAD_ALT_1_PATCH
|
|
if (c->tags != SCRATCHPAD_MASK)
|
|
c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
|
|
#else
|
|
c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
|
|
#endif // EMPTYVIEW_PATCH
|
|
}
|
|
|
|
int
|
|
applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
|
|
{
|
|
int baseismin;
|
|
Monitor *m = c->mon;
|
|
|
|
/* set minimum possible */
|
|
*w = MAX(1, *w);
|
|
*h = MAX(1, *h);
|
|
if (interact) {
|
|
if (*x > sw)
|
|
*x = sw - WIDTH(c);
|
|
if (*y > sh)
|
|
*y = sh - HEIGHT(c);
|
|
if (*x + *w + 2 * c->bw < 0)
|
|
*x = 0;
|
|
if (*y + *h + 2 * c->bw < 0)
|
|
*y = 0;
|
|
} else {
|
|
if (*x >= m->wx + m->ww)
|
|
*x = m->wx + m->ww - WIDTH(c);
|
|
if (*y >= m->wy + m->wh)
|
|
*y = m->wy + m->wh - HEIGHT(c);
|
|
if (*x + *w + 2 * c->bw <= m->wx)
|
|
*x = m->wx;
|
|
if (*y + *h + 2 * c->bw <= m->wy)
|
|
*y = m->wy;
|
|
}
|
|
if (*h < bh)
|
|
*h = bh;
|
|
if (*w < bh)
|
|
*w = bh;
|
|
if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
|
|
if (!c->hintsvalid)
|
|
updatesizehints(c);
|
|
/* see last two sentences in ICCCM 4.1.2.3 */
|
|
baseismin = c->basew == c->minw && c->baseh == c->minh;
|
|
if (!baseismin) { /* temporarily remove base dimensions */
|
|
*w -= c->basew;
|
|
*h -= c->baseh;
|
|
}
|
|
/* adjust for aspect limits */
|
|
if (c->mina > 0 && c->maxa > 0) {
|
|
if (c->maxa < (float)*w / *h)
|
|
*w = *h * c->maxa + 0.5;
|
|
else if (c->mina < (float)*h / *w)
|
|
*h = *w * c->mina + 0.5;
|
|
}
|
|
if (baseismin) { /* increment calculation requires this */
|
|
*w -= c->basew;
|
|
*h -= c->baseh;
|
|
}
|
|
/* adjust for increment value */
|
|
if (c->incw)
|
|
*w -= *w % c->incw;
|
|
if (c->inch)
|
|
*h -= *h % c->inch;
|
|
/* restore base dimensions */
|
|
*w = MAX(*w + c->basew, c->minw);
|
|
*h = MAX(*h + c->baseh, c->minh);
|
|
if (c->maxw)
|
|
*w = MIN(*w, c->maxw);
|
|
if (c->maxh)
|
|
*h = MIN(*h, c->maxh);
|
|
}
|
|
return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
|
|
}
|
|
|
|
void
|
|
arrange(Monitor *m)
|
|
{
|
|
if (m)
|
|
showhide(m->stack);
|
|
else for (m = mons; m; m = m->next)
|
|
showhide(m->stack);
|
|
if (m) {
|
|
arrangemon(m);
|
|
restack(m);
|
|
} else for (m = mons; m; m = m->next)
|
|
arrangemon(m);
|
|
}
|
|
|
|
void
|
|
arrangemon(Monitor *m)
|
|
{
|
|
#if BAR_PADDING_SMART_PATCH
|
|
updatebarpos(selmon);
|
|
for (Bar *bar = selmon->bar; bar; bar = bar->next)
|
|
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
|
|
#endif // BAR_PADDING_SMART_PATCH
|
|
#if TAB_PATCH
|
|
updatebarpos(m);
|
|
XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
|
|
#endif // TAB_PATCH
|
|
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
|
|
if (m->lt[m->sellt]->arrange)
|
|
m->lt[m->sellt]->arrange(m);
|
|
}
|
|
|
|
void
|
|
attach(Client *c)
|
|
{
|
|
c->next = c->mon->clients;
|
|
c->mon->clients = c;
|
|
}
|
|
|
|
void
|
|
attachstack(Client *c)
|
|
{
|
|
c->snext = c->mon->stack;
|
|
c->mon->stack = c;
|
|
}
|
|
|
|
void
|
|
buttonpress(XEvent *e)
|
|
{
|
|
int click, i, r;
|
|
#if TAB_PATCH
|
|
int x;
|
|
#endif // TAB_PATCH
|
|
Arg arg = {0};
|
|
Client *c;
|
|
Monitor *m;
|
|
Bar *bar;
|
|
XButtonPressedEvent *ev = &e->xbutton;
|
|
const BarRule *br;
|
|
BarArg carg = { 0, 0, 0, 0 };
|
|
click = ClkRootWin;
|
|
|
|
#if BAR_STATUSCMD_PATCH && !BAR_DWMBLOCKS_PATCH
|
|
*lastbutton = '0' + ev->button;
|
|
#endif // BAR_STATUSCMD_PATCH | BAR_DWMBLOCKS_PATCH
|
|
|
|
/* focus monitor if necessary */
|
|
if ((m = wintomon(ev->window)) && m != selmon
|
|
#if FOCUSONCLICK_PATCH
|
|
&& (focusonwheel || (ev->button != Button4 && ev->button != Button5))
|
|
#endif // FOCUSONCLICK_PATCH
|
|
) {
|
|
unfocus(selmon->sel, 1, NULL);
|
|
selmon = m;
|
|
focus(NULL);
|
|
}
|
|
|
|
for (bar = selmon->bar; bar; bar = bar->next) {
|
|
if (ev->window == bar->win) {
|
|
for (r = 0; r < LENGTH(barrules); r++) {
|
|
br = &barrules[r];
|
|
if (br->bar != bar->idx || (br->monitor == 'A' && m != selmon) || br->clickfunc == NULL)
|
|
continue;
|
|
if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num)
|
|
continue;
|
|
if (bar->x[r] <= ev->x && ev->x <= bar->x[r] + bar->w[r]) {
|
|
carg.x = ev->x - bar->x[r];
|
|
carg.y = ev->y - bar->borderpx;
|
|
carg.w = bar->w[r];
|
|
carg.h = bar->bh - 2 * bar->borderpx;
|
|
click = br->clickfunc(bar, &arg, &carg);
|
|
if (click < 0)
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if TAB_PATCH
|
|
if (ev->window == selmon->tabwin) {
|
|
for (i = 0, x = 0, c = selmon->clients; c; c = c->next) {
|
|
if (!ISVISIBLE(c) || HIDDEN(c))
|
|
continue;
|
|
x += selmon->tab_widths[i];
|
|
if (ev->x > x)
|
|
++i;
|
|
else
|
|
break;
|
|
if (i >= m->ntabs)
|
|
break;
|
|
}
|
|
if (c) {
|
|
click = ClkTabBar;
|
|
arg.ui = i;
|
|
}
|
|
}
|
|
#endif // TAB_PATCH
|
|
|
|
if (click == ClkRootWin && (c = wintoclient(ev->window))) {
|
|
#if FOCUSONCLICK_PATCH
|
|
if (focusonwheel || (ev->button != Button4 && ev->button != Button5))
|
|
focus(c);
|
|
#else
|
|
focus(c);
|
|
restack(selmon);
|
|
#endif // FOCUSONCLICK_PATCH
|
|
XAllowEvents(dpy, ReplayPointer, CurrentTime);
|
|
click = ClkClientWin;
|
|
}
|
|
|
|
for (i = 0; i < LENGTH(buttons); i++) {
|
|
if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
|
|
&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) {
|
|
buttons[i].func(
|
|
(
|
|
click == ClkTagBar
|
|
#if TAB_PATCH
|
|
|| click == ClkTabBar
|
|
#endif // TAB_PATCH
|
|
#if BAR_WINTITLEACTIONS_PATCH
|
|
|| click == ClkWinTitle
|
|
#endif // BAR_WINTITLEACTIONS_PATCH
|
|
) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
checkotherwm(void)
|
|
{
|
|
xerrorxlib = XSetErrorHandler(xerrorstart);
|
|
/* this causes an error if some other window manager is running */
|
|
XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
|
|
XSync(dpy, False);
|
|
XSetErrorHandler(xerror);
|
|
XSync(dpy, False);
|
|
}
|
|
|
|
void
|
|
cleanup(void)
|
|
{
|
|
Monitor *m;
|
|
Layout foo = { "", NULL };
|
|
size_t i;
|
|
|
|
#if ALT_TAB_PATCH
|
|
alttabend();
|
|
#endif // ALT_TAB_PATCH
|
|
|
|
#if SEAMLESS_RESTART_PATCH
|
|
for (m = mons; m; m = m->next)
|
|
persistmonitorstate(m);
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
|
|
#if COOL_AUTOSTART_PATCH
|
|
/* kill child processes */
|
|
for (i = 0; i < autostart_len; i++) {
|
|
if (0 < autostart_pids[i]) {
|
|
kill(autostart_pids[i], SIGTERM);
|
|
waitpid(autostart_pids[i], NULL, 0);
|
|
}
|
|
}
|
|
#endif // COOL_AUTOSTART_PATCH
|
|
|
|
selmon->lt[selmon->sellt] = &foo;
|
|
for (m = mons; m; m = m->next)
|
|
while (m->stack)
|
|
unmanage(m->stack, 0);
|
|
XUngrabKey(dpy, AnyKey, AnyModifier, root);
|
|
while (mons)
|
|
cleanupmon(mons);
|
|
#if BAR_SYSTRAY_PATCH
|
|
if (showsystray && systray) {
|
|
while (systray->icons)
|
|
removesystrayicon(systray->icons);
|
|
if (systray->win) {
|
|
XUnmapWindow(dpy, systray->win);
|
|
XDestroyWindow(dpy, systray->win);
|
|
}
|
|
free(systray);
|
|
}
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
for (i = 0; i < CurLast; i++)
|
|
drw_cur_free(drw, cursor[i]);
|
|
#if BAR_STATUS2D_PATCH && !BAR_STATUSCOLORS_PATCH
|
|
for (i = 0; i < LENGTH(colors) + 1; i++)
|
|
#else
|
|
for (i = 0; i < LENGTH(colors); i++)
|
|
#endif // BAR_STATUS2D_PATCH
|
|
free(scheme[i]);
|
|
free(scheme);
|
|
XDestroyWindow(dpy, wmcheckwin);
|
|
drw_free(drw);
|
|
XSync(dpy, False);
|
|
XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
|
|
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
|
|
|
|
#if IPC_PATCH
|
|
ipc_cleanup();
|
|
|
|
if (close(epoll_fd) < 0)
|
|
fprintf(stderr, "Failed to close epoll file descriptor\n");
|
|
#endif // IPC_PATCH
|
|
}
|
|
|
|
void
|
|
cleanupmon(Monitor *mon)
|
|
{
|
|
Monitor *m;
|
|
Bar *bar;
|
|
|
|
if (mon == mons)
|
|
mons = mons->next;
|
|
else {
|
|
for (m = mons; m && m->next != mon; m = m->next);
|
|
m->next = mon->next;
|
|
}
|
|
for (bar = mon->bar; bar; bar = mon->bar) {
|
|
if (!bar->external) {
|
|
XUnmapWindow(dpy, bar->win);
|
|
XDestroyWindow(dpy, bar->win);
|
|
}
|
|
mon->bar = bar->next;
|
|
#if BAR_SYSTRAY_PATCH
|
|
if (systray && bar == systray->bar)
|
|
systray->bar = NULL;
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
free(bar);
|
|
}
|
|
#if TAB_PATCH
|
|
XUnmapWindow(dpy, mon->tabwin);
|
|
XDestroyWindow(dpy, mon->tabwin);
|
|
#endif // TAB_PATCH
|
|
#if PERTAG_PATCH
|
|
free(mon->pertag);
|
|
#endif // PERTAG_PATCH
|
|
#if BAR_TAGPREVIEW_PATCH
|
|
for (size_t i = 0; i < NUMTAGS; i++)
|
|
if (mon->tagmap[i])
|
|
XFreePixmap(dpy, mon->tagmap[i]);
|
|
XUnmapWindow(dpy, mon->tagwin);
|
|
XDestroyWindow(dpy, mon->tagwin);
|
|
#endif // BAR_TAGPREVIEW_PATCH
|
|
free(mon);
|
|
}
|
|
|
|
void
|
|
clientmessage(XEvent *e)
|
|
{
|
|
#if BAR_SYSTRAY_PATCH
|
|
XWindowAttributes wa;
|
|
XSetWindowAttributes swa;
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
XClientMessageEvent *cme = &e->xclient;
|
|
Client *c = wintoclient(cme->window);
|
|
#if FOCUSONNETACTIVE_PATCH
|
|
unsigned int i;
|
|
#endif // FOCUSONNETACTIVE_PATCH
|
|
|
|
#if BAR_SYSTRAY_PATCH
|
|
if (showsystray && systray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
|
|
/* add systray icons */
|
|
if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
|
|
if (!(c = (Client *)calloc(1, sizeof(Client))))
|
|
die("fatal: could not malloc() %u bytes\n", sizeof(Client));
|
|
if (!(c->win = cme->data.l[2])) {
|
|
free(c);
|
|
return;
|
|
}
|
|
|
|
c->mon = selmon;
|
|
c->next = systray->icons;
|
|
systray->icons = c;
|
|
XGetWindowAttributes(dpy, c->win, &wa);
|
|
c->x = c->oldx = c->y = c->oldy = 0;
|
|
c->w = c->oldw = wa.width;
|
|
c->h = c->oldh = wa.height;
|
|
c->oldbw = wa.border_width;
|
|
c->bw = 0;
|
|
c->isfloating = True;
|
|
/* reuse tags field as mapped status */
|
|
c->tags = 1;
|
|
updatesizehints(c);
|
|
updatesystrayicongeom(c, wa.width, wa.height);
|
|
XAddToSaveSet(dpy, c->win);
|
|
XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
|
|
XClassHint ch = {"dwmsystray", "dwmsystray"};
|
|
XSetClassHint(dpy, c->win, &ch);
|
|
XReparentWindow(dpy, c->win, systray->win, 0, 0);
|
|
/* use parents background color */
|
|
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
|
|
XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
|
|
sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
|
|
XSync(dpy, False);
|
|
setclientstate(c, NormalState);
|
|
}
|
|
return;
|
|
}
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
|
|
if (!c)
|
|
return;
|
|
if (cme->message_type == netatom[NetWMState]) {
|
|
if (cme->data.l[1] == netatom[NetWMFullscreen]
|
|
|| cme->data.l[2] == netatom[NetWMFullscreen]) {
|
|
#if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
|
|
if (c->fakefullscreen == 2 && c->isfullscreen)
|
|
c->fakefullscreen = 3;
|
|
#endif // FAKEFULLSCREEN_CLIENT_PATCH
|
|
setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
|
|
|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */
|
|
#if !FAKEFULLSCREEN_PATCH
|
|
&& !c->isfullscreen
|
|
#endif // !FAKEFULLSCREEN_PATCH
|
|
)));
|
|
}
|
|
} else if (cme->message_type == netatom[NetActiveWindow]) {
|
|
#if FOCUSONNETACTIVE_PATCH
|
|
if (c->tags & c->mon->tagset[c->mon->seltags])
|
|
focus(c);
|
|
else {
|
|
for (i = 0; i < NUMTAGS && !((1 << i) & c->tags); i++);
|
|
if (i < NUMTAGS) {
|
|
if (c != selmon->sel)
|
|
unfocus(selmon->sel, 0, NULL);
|
|
selmon = c->mon;
|
|
if (((1 << i) & TAGMASK) != selmon->tagset[selmon->seltags])
|
|
view(&((Arg) { .ui = 1 << i }));
|
|
focus(c);
|
|
restack(selmon);
|
|
}
|
|
}
|
|
#else
|
|
if (c != selmon->sel && !c->isurgent)
|
|
seturgent(c, 1);
|
|
#endif // FOCUSONNETACTIVE_PATCH
|
|
}
|
|
}
|
|
|
|
void
|
|
configure(Client *c)
|
|
{
|
|
XConfigureEvent ce;
|
|
|
|
ce.type = ConfigureNotify;
|
|
ce.display = dpy;
|
|
ce.event = c->win;
|
|
ce.window = c->win;
|
|
ce.x = c->x;
|
|
ce.y = c->y;
|
|
ce.width = c->w;
|
|
ce.height = c->h;
|
|
ce.border_width = c->bw;
|
|
|
|
#if NOBORDER_PATCH
|
|
if (noborder(c)) {
|
|
ce.width += c->bw * 2;
|
|
ce.height += c->bw * 2;
|
|
ce.border_width = 0;
|
|
}
|
|
#endif // NOBORDER_PATCH
|
|
|
|
ce.above = None;
|
|
ce.override_redirect = False;
|
|
XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
|
|
}
|
|
|
|
void
|
|
configurenotify(XEvent *e)
|
|
{
|
|
Monitor *m;
|
|
Bar *bar;
|
|
#if !FAKEFULLSCREEN_PATCH
|
|
Client *c;
|
|
#endif // !FAKEFULLSCREEN_PATCH
|
|
XConfigureEvent *ev = &e->xconfigure;
|
|
int dirty;
|
|
/* TODO: updategeom handling sucks, needs to be simplified */
|
|
if (ev->window == root) {
|
|
dirty = (sw != ev->width || sh != ev->height);
|
|
sw = ev->width;
|
|
sh = ev->height;
|
|
if (updategeom() || dirty) {
|
|
drw_resize(drw, sw, sh);
|
|
updatebars();
|
|
for (m = mons; m; m = m->next) {
|
|
#if !FAKEFULLSCREEN_PATCH
|
|
for (c = m->clients; c; c = c->next)
|
|
#if FAKEFULLSCREEN_CLIENT_PATCH
|
|
if (c->isfullscreen && c->fakefullscreen != 1)
|
|
#else
|
|
if (c->isfullscreen)
|
|
#endif // FAKEFULLSCREEN_CLIENT_PATCH
|
|
resizeclient(c, m->mx, m->my, m->mw, m->mh);
|
|
#endif // !FAKEFULLSCREEN_PATCH
|
|
for (bar = m->bar; bar; bar = bar->next)
|
|
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
|
|
#if BAR_TAGPREVIEW_PATCH
|
|
createpreview(m);
|
|
#endif // BAR_TAGPREVIEW_PATCH
|
|
}
|
|
arrange(NULL);
|
|
focus(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
configurerequest(XEvent *e)
|
|
{
|
|
Client *c;
|
|
Monitor *m;
|
|
#if BAR_ANYBAR_PATCH
|
|
Bar *bar;
|
|
#endif // BAR_ANYBAR_PATCH
|
|
XConfigureRequestEvent *ev = &e->xconfigurerequest;
|
|
XWindowChanges wc;
|
|
|
|
if (ignoreconfigurerequests)
|
|
return;
|
|
|
|
if ((c = wintoclient(ev->window))) {
|
|
if (ev->value_mask & CWBorderWidth)
|
|
c->bw = ev->border_width;
|
|
else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
|
|
m = c->mon;
|
|
#if STEAM_PATCH
|
|
if (!c->issteam) {
|
|
if (ev->value_mask & CWX) {
|
|
c->oldx = c->x;
|
|
c->x = m->mx + ev->x;
|
|
}
|
|
if (ev->value_mask & CWY) {
|
|
c->oldy = c->y;
|
|
c->y = m->my + ev->y;
|
|
}
|
|
}
|
|
#else
|
|
if (ev->value_mask & CWX) {
|
|
c->oldx = c->x;
|
|
c->x = m->mx + ev->x;
|
|
}
|
|
if (ev->value_mask & CWY) {
|
|
c->oldy = c->y;
|
|
c->y = m->my + ev->y;
|
|
}
|
|
#endif // STEAM_PATCH
|
|
if (ev->value_mask & CWWidth) {
|
|
c->oldw = c->w;
|
|
c->w = ev->width;
|
|
}
|
|
if (ev->value_mask & CWHeight) {
|
|
c->oldh = c->h;
|
|
c->h = ev->height;
|
|
}
|
|
if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
|
|
c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
|
|
if ((c->y + c->h) > m->my + m->mh && c->isfloating)
|
|
c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
|
|
if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
|
|
configure(c);
|
|
if (ISVISIBLE(c))
|
|
XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
|
|
#if AUTORESIZE_PATCH
|
|
else
|
|
c->needresize = 1;
|
|
#endif // AUTORESIZE_PATCH
|
|
} else
|
|
configure(c);
|
|
} else {
|
|
wc.x = ev->x;
|
|
wc.y = ev->y;
|
|
#if BAR_ANYBAR_PATCH
|
|
m = wintomon(ev->window);
|
|
for (bar = m->bar; bar; bar = bar->next) {
|
|
if (bar->win == ev->window) {
|
|
wc.y = bar->by;
|
|
wc.x = bar->bx;
|
|
}
|
|
}
|
|
#endif // BAR_ANYBAR_PATCH
|
|
wc.width = ev->width;
|
|
wc.height = ev->height;
|
|
wc.border_width = ev->border_width;
|
|
wc.sibling = ev->above;
|
|
wc.stack_mode = ev->detail;
|
|
XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
|
|
}
|
|
XSync(dpy, False);
|
|
}
|
|
|
|
Monitor *
|
|
createmon(void)
|
|
{
|
|
Monitor *m, *mon;
|
|
int i, n, mi, max_bars = 2, istopbar = topbar;
|
|
#if MONITOR_RULES_PATCH
|
|
int layout;
|
|
#endif // MONITOR_RULES_PATCH
|
|
|
|
const BarRule *br;
|
|
Bar *bar;
|
|
#if MONITOR_RULES_PATCH
|
|
int j;
|
|
const MonitorRule *mr;
|
|
#endif // MONITOR_RULES_PATCH
|
|
|
|
m = ecalloc(1, sizeof(Monitor));
|
|
#if !EMPTYVIEW_PATCH
|
|
#if VIEW_HISTORY_PATCH
|
|
for (i = 0; i < LENGTH(m->tagset); i++)
|
|
m->tagset[i] = 1;
|
|
#else
|
|
m->tagset[0] = m->tagset[1] = 1;
|
|
#endif // VIEW_HISTORY_PATCH
|
|
#endif // EMPTYVIEW_PATCH
|
|
m->mfact = mfact;
|
|
m->nmaster = nmaster;
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
m->nstack = nstack;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
m->showbar = showbar;
|
|
#if TAB_PATCH
|
|
m->showtab = showtab;
|
|
m->toptab = toptab;
|
|
m->ntabs = 0;
|
|
#endif // TAB_PATCH
|
|
#if SETBORDERPX_PATCH
|
|
m->borderpx = borderpx;
|
|
#endif // SETBORDERPX_PATCH
|
|
#if VANITYGAPS_PATCH
|
|
m->gappih = gappih;
|
|
m->gappiv = gappiv;
|
|
m->gappoh = gappoh;
|
|
m->gappov = gappov;
|
|
#endif // VANITYGAPS_PATCH
|
|
for (mi = 0, mon = mons; mon; mon = mon->next, mi++); // monitor index
|
|
m->num = mi;
|
|
#if MONITOR_RULES_PATCH
|
|
for (j = 0; j < LENGTH(monrules); j++) {
|
|
mr = &monrules[j];
|
|
if ((mr->monitor == -1 || mr->monitor == m->num)
|
|
#if PERTAG_PATCH
|
|
&& (mr->tag <= 0 || (m->tagset[0] & (1 << (mr->tag - 1))))
|
|
#endif // PERTAG_PATCH
|
|
) {
|
|
layout = MAX(mr->layout, 0);
|
|
layout = MIN(layout, LENGTH(layouts) - 1);
|
|
m->lt[0] = &layouts[layout];
|
|
m->lt[1] = &layouts[1 % LENGTH(layouts)];
|
|
strncpy(m->ltsymbol, layouts[layout].symbol, sizeof m->ltsymbol);
|
|
|
|
if (mr->mfact > -1)
|
|
m->mfact = mr->mfact;
|
|
if (mr->nmaster > -1)
|
|
m->nmaster = mr->nmaster;
|
|
if (mr->showbar > -1)
|
|
m->showbar = mr->showbar;
|
|
if (mr->topbar > -1)
|
|
istopbar = mr->topbar;
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
m->lt[0] = &layouts[0];
|
|
m->lt[1] = &layouts[1 % LENGTH(layouts)];
|
|
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
|
|
#endif // MONITOR_RULES_PATCH
|
|
|
|
/* Derive the number of bars for this monitor based on bar rules */
|
|
for (n = -1, i = 0; i < LENGTH(barrules); i++) {
|
|
br = &barrules[i];
|
|
if (br->monitor == 'A' || br->monitor == -1 || br->monitor == m->num)
|
|
n = MAX(br->bar, n);
|
|
}
|
|
|
|
m->bar = NULL;
|
|
for (i = 0; i <= n && i < max_bars; i++) {
|
|
bar = ecalloc(1, sizeof(Bar));
|
|
bar->mon = m;
|
|
bar->idx = i;
|
|
bar->next = m->bar;
|
|
bar->topbar = istopbar;
|
|
m->bar = bar;
|
|
istopbar = !istopbar;
|
|
bar->showbar = 1;
|
|
bar->external = 0;
|
|
#if BAR_BORDER_PATCH
|
|
bar->borderpx = (barborderpx ? barborderpx : borderpx);
|
|
#else
|
|
bar->borderpx = 0;
|
|
#endif // BAR_BORDER_PATCH
|
|
bar->bh = bh + bar->borderpx * 2;
|
|
bar->borderscheme = SchemeNorm;
|
|
}
|
|
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
m->ltaxis[LAYOUT] = m->lt[0]->preset.layout;
|
|
m->ltaxis[MASTER] = m->lt[0]->preset.masteraxis;
|
|
m->ltaxis[STACK] = m->lt[0]->preset.stack1axis;
|
|
m->ltaxis[STACK2] = m->lt[0]->preset.stack2axis;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
|
|
#if PERTAG_PATCH
|
|
if (!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag))))
|
|
die("fatal: could not malloc() %u bytes\n", sizeof(Pertag));
|
|
m->pertag->curtag = 1;
|
|
for (i = 0; i <= NUMTAGS; i++) {
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
m->pertag->nstacks[i] = m->nstack;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
|
|
#if !MONITOR_RULES_PATCH
|
|
/* init nmaster */
|
|
m->pertag->nmasters[i] = m->nmaster;
|
|
|
|
/* init mfacts */
|
|
m->pertag->mfacts[i] = m->mfact;
|
|
|
|
#if PERTAGBAR_PATCH
|
|
/* init showbar */
|
|
m->pertag->showbars[i] = m->showbar;
|
|
#endif // PERTAGBAR_PATCH
|
|
#endif // MONITOR_RULES_PATCH
|
|
|
|
#if ZOOMSWAP_PATCH
|
|
m->pertag->prevzooms[i] = NULL;
|
|
#endif // ZOOMSWAP_PATCH
|
|
|
|
/* init layouts */
|
|
#if MONITOR_RULES_PATCH
|
|
for (j = 0; j < LENGTH(monrules); j++) {
|
|
mr = &monrules[j];
|
|
if ((mr->monitor == -1 || mr->monitor == m->num) && (mr->tag == -1 || mr->tag == i)) {
|
|
layout = MAX(mr->layout, 0);
|
|
layout = MIN(layout, LENGTH(layouts) - 1);
|
|
m->pertag->ltidxs[i][0] = &layouts[layout];
|
|
m->pertag->ltidxs[i][1] = m->lt[0];
|
|
m->pertag->nmasters[i] = (mr->nmaster > -1 ? mr->nmaster : m->nmaster);
|
|
m->pertag->mfacts[i] = (mr->mfact > -1 ? mr->mfact : m->mfact);
|
|
#if PERTAGBAR_PATCH
|
|
m->pertag->showbars[i] = (mr->showbar > -1 ? mr->showbar : m->showbar);
|
|
#endif // PERTAGBAR_PATCH
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
m->pertag->ltaxis[i][LAYOUT] = m->pertag->ltidxs[i][0]->preset.layout;
|
|
m->pertag->ltaxis[i][MASTER] = m->pertag->ltidxs[i][0]->preset.masteraxis;
|
|
m->pertag->ltaxis[i][STACK] = m->pertag->ltidxs[i][0]->preset.stack1axis;
|
|
m->pertag->ltaxis[i][STACK2] = m->pertag->ltidxs[i][0]->preset.stack2axis;
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
m->pertag->ltidxs[i][0] = m->lt[0];
|
|
m->pertag->ltidxs[i][1] = m->lt[1];
|
|
#if FLEXTILE_DELUXE_LAYOUT
|
|
/* init flextile axes */
|
|
m->pertag->ltaxis[i][LAYOUT] = m->ltaxis[LAYOUT];
|
|
m->pertag->ltaxis[i][MASTER] = m->ltaxis[MASTER];
|
|
m->pertag->ltaxis[i][STACK] = m->ltaxis[STACK];
|
|
m->pertag->ltaxis[i][STACK2] = m->ltaxis[STACK2];
|
|
#endif // FLEXTILE_DELUXE_LAYOUT
|
|
#endif // MONITOR_RULES_PATCH
|
|
m->pertag->sellts[i] = m->sellt;
|
|
|
|
#if PERTAG_VANITYGAPS_PATCH && VANITYGAPS_PATCH
|
|
m->pertag->enablegaps[i] = 1;
|
|
m->pertag->gaps[i] =
|
|
((gappoh & 0xFF) << 0) | ((gappov & 0xFF) << 8) | ((gappih & 0xFF) << 16) | ((gappiv & 0xFF) << 24);
|
|
#endif // PERTAG_VANITYGAPS_PATCH | VANITYGAPS_PATCH
|
|
}
|
|
#endif // PERTAG_PATCH
|
|
|
|
#if PERMON_VANITYGAPS_PATCH
|
|
m->enablegaps = 1;
|
|
#endif // PERMON_VANITYGAPS_PATCH
|
|
|
|
#if SEAMLESS_RESTART_PATCH
|
|
restoremonitorstate(m);
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
|
|
#if INSETS_PATCH
|
|
m->inset = default_inset;
|
|
#endif // INSETS_PATCH
|
|
return m;
|
|
}
|
|
|
|
void
|
|
destroynotify(XEvent *e)
|
|
{
|
|
Client *c;
|
|
#if BAR_ANYBAR_PATCH
|
|
Monitor *m;
|
|
Bar *bar;
|
|
#endif // BAR_ANYBAR_PATCH
|
|
XDestroyWindowEvent *ev = &e->xdestroywindow;
|
|
|
|
if ((c = wintoclient(ev->window)))
|
|
unmanage(c, 1);
|
|
#if SWALLOW_PATCH
|
|
else if ((c = swallowingclient(ev->window)))
|
|
unmanage(c->swallowing, 1);
|
|
#endif // SWALLOW_PATCH
|
|
#if BAR_SYSTRAY_PATCH
|
|
else if (showsystray && (c = wintosystrayicon(ev->window))) {
|
|
removesystrayicon(c);
|
|
drawbarwin(systray->bar);
|
|
}
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
#if BAR_ANYBAR_PATCH
|
|
else {
|
|
m = wintomon(ev->window);
|
|
for (bar = m->bar; bar; bar = bar->next) {
|
|
if (bar->win == ev->window) {
|
|
unmanagealtbar(ev->window);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif // BAR_ANYBAR_PATCH
|
|
}
|
|
|
|
void
|
|
detach(Client *c)
|
|
{
|
|
Client **tc;
|
|
#if SEAMLESS_RESTART_PATCH
|
|
c->idx = 0;
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
#if FOCUSMASTER_RETURN_PATCH
|
|
for (int i = 1; i < NUMTAGS; i++)
|
|
if (c == c->mon->tagmarked[i])
|
|
c->mon->tagmarked[i] = NULL;
|
|
#endif // FOCUSMASTER_RETURN_PATCH
|
|
|
|
for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
|
|
*tc = c->next;
|
|
c->next = NULL;
|
|
}
|
|
|
|
void
|
|
detachstack(Client *c)
|
|
{
|
|
Client **tc, *t;
|
|
|
|
for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
|
|
*tc = c->snext;
|
|
|
|
if (c == c->mon->sel) {
|
|
for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
|
|
c->mon->sel = t;
|
|
}
|
|
c->snext = NULL;
|
|
}
|
|
|
|
Monitor *
|
|
dirtomon(int dir)
|
|
{
|
|
Monitor *m = NULL;
|
|
|
|
if (dir > 0) {
|
|
if (!(m = selmon->next))
|
|
m = mons;
|
|
} else if (selmon == mons)
|
|
for (m = mons; m->next; m = m->next);
|
|
else
|
|
for (m = mons; m->next != selmon; m = m->next);
|
|
return m;
|
|
}
|
|
|
|
void
|
|
drawbar(Monitor *m)
|
|
{
|
|
Bar *bar;
|
|
|
|
#if !BAR_FLEXWINTITLE_PATCH
|
|
if (m->showbar)
|
|
#endif // BAR_FLEXWINTITLE_PATCH
|
|
for (bar = m->bar; bar; bar = bar->next)
|
|
drawbarwin(bar);
|
|
}
|
|
|
|
void
|
|
drawbars(void)
|
|
{
|
|
Monitor *m;
|
|
for (m = mons; m; m = m->next)
|
|
drawbar(m);
|
|
}
|
|
|
|
void
|
|
drawbarwin(Bar *bar)
|
|
{
|
|
if (!bar || !bar->win || bar->external)
|
|
return;
|
|
int r, w, total_drawn = 0;
|
|
int rx, lx, rw, lw; // bar size, split between left and right if a center module is added
|
|
const BarRule *br;
|
|
|
|
if (bar->borderpx) {
|
|
XSetForeground(drw->dpy, drw->gc, scheme[bar->borderscheme][ColBorder].pixel);
|
|
XFillRectangle(drw->dpy, drw->drawable, drw->gc, 0, 0, bar->bw, bar->bh);
|
|
}
|
|
|
|
BarArg warg = { 0 };
|
|
BarArg darg = { 0 };
|
|
warg.h = bar->bh - 2 * bar->borderpx;
|
|
|
|
rw = lw = bar->bw - 2 * bar->borderpx;
|
|
rx = lx = bar->borderpx;
|
|
|
|
drw_setscheme(drw, scheme[SchemeNorm]);
|
|
drw_rect(drw, lx, bar->borderpx, lw, bar->bh - 2 * bar->borderpx, 1, 1);
|
|
for (r = 0; r < LENGTH(barrules); r++) {
|
|
br = &barrules[r];
|
|
if (br->bar != bar->idx || !br->widthfunc || (br->monitor == 'A' && bar->mon != selmon))
|
|
continue;
|
|
if (br->monitor != 'A' && br->monitor != -1 && br->monitor != bar->mon->num)
|
|
continue;
|
|
drw_setscheme(drw, scheme[SchemeNorm]);
|
|
warg.w = (br->alignment < BAR_ALIGN_RIGHT_LEFT ? lw : rw);
|
|
|
|
w = br->widthfunc(bar, &warg);
|
|
w = MIN(warg.w, w);
|
|
|
|
if (lw <= 0) { // if left is exhausted then switch to right side, and vice versa
|
|
lw = rw;
|
|
lx = rx;
|
|
} else if (rw <= 0) {
|
|
rw = lw;
|
|
rx = lx;
|
|
}
|
|
|
|
switch(br->alignment) {
|
|
default:
|
|
case BAR_ALIGN_NONE:
|
|
case BAR_ALIGN_LEFT_LEFT:
|
|
case BAR_ALIGN_LEFT:
|
|
bar->x[r] = lx;
|
|
if (lx == rx) {
|
|
rx += w;
|
|
rw -= w;
|
|
}
|
|
lx += w;
|
|
lw -= w;
|
|
break;
|
|
case BAR_ALIGN_LEFT_RIGHT:
|
|
case BAR_ALIGN_RIGHT:
|
|
bar->x[r] = lx + lw - w;
|
|
if (lx == rx)
|
|
rw -= w;
|
|
lw -= w;
|
|
break;
|
|
case BAR_ALIGN_LEFT_CENTER:
|
|
case BAR_ALIGN_CENTER:
|
|
bar->x[r] = lx + lw / 2 - w / 2;
|
|
if (lx == rx) {
|
|
rw = rx + rw - bar->x[r] - w;
|
|
rx = bar->x[r] + w;
|
|
}
|
|
lw = bar->x[r] - lx;
|
|
break;
|
|
case BAR_ALIGN_RIGHT_LEFT:
|
|
bar->x[r] = rx;
|
|
if (lx == rx) {
|
|
lx += w;
|
|
lw -= w;
|
|
}
|
|
rx += w;
|
|
rw -= w;
|
|
break;
|
|
case BAR_ALIGN_RIGHT_RIGHT:
|
|
bar->x[r] = rx + rw - w;
|
|
if (lx == rx)
|
|
lw -= w;
|
|
rw -= w;
|
|
break;
|
|
case BAR_ALIGN_RIGHT_CENTER:
|
|
bar->x[r] = rx + rw / 2 - w / 2;
|
|
if (lx == rx) {
|
|
lw = lx + lw - bar->x[r] + w;
|
|
lx = bar->x[r] + w;
|
|
}
|
|
rw = bar->x[r] - rx;
|
|
break;
|
|
}
|
|
bar->w[r] = w;
|
|
darg.x = bar->x[r];
|
|
darg.y = bar->borderpx;
|
|
darg.h = bar->bh - 2 * bar->borderpx;
|
|
darg.w = bar->w[r];
|
|
if (br->drawfunc)
|
|
total_drawn += br->drawfunc(bar, &darg);
|
|
}
|
|
|
|
if (total_drawn == 0 && bar->showbar) {
|
|
bar->showbar = 0;
|
|
updatebarpos(bar->mon);
|
|
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
|
|
arrange(bar->mon);
|
|
}
|
|
else if (total_drawn > 0 && !bar->showbar) {
|
|
bar->showbar = 1;
|
|
updatebarpos(bar->mon);
|
|
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
|
|
drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh);
|
|
arrange(bar->mon);
|
|
} else
|
|
drw_map(drw, bar->win, 0, 0, bar->bw, bar->bh);
|
|
}
|
|
|
|
#if !FOCUSONCLICK_PATCH
|
|
void
|
|
enternotify(XEvent *e)
|
|
{
|
|
Client *c;
|
|
#if LOSEFULLSCREEN_PATCH
|
|
Client *sel;
|
|
#endif // LOSEFULLSCREEN_PATCH
|
|
Monitor *m;
|
|
XCrossingEvent *ev = &e->xcrossing;
|
|
|
|
if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
|
|
return;
|
|
c = wintoclient(ev->window);
|
|
m = c ? c->mon : wintomon(ev->window);
|
|
if (m != selmon) {
|
|
#if LOSEFULLSCREEN_PATCH
|
|
sel = selmon->sel;
|
|
selmon = m;
|
|
unfocus(sel, 1, c);
|
|
#else
|
|
unfocus(selmon->sel, 1, c);
|
|
selmon = m;
|
|
#endif // LOSEFULLSCREEN_PATCH
|
|
} else if (!c || c == selmon->sel)
|
|
return;
|
|
focus(c);
|
|
}
|
|
#endif // FOCUSONCLICK_PATCH
|
|
|
|
void
|
|
expose(XEvent *e)
|
|
{
|
|
Monitor *m;
|
|
XExposeEvent *ev = &e->xexpose;
|
|
|
|
if (ev->count == 0 && (m = wintomon(ev->window))) {
|
|
drawbar(m);
|
|
#if TAB_PATCH
|
|
drawtabs();
|
|
#endif // TAB_PATCH
|
|
}
|
|
}
|
|
|
|
void
|
|
focus(Client *c)
|
|
{
|
|
#if FOCUSFOLLOWMOUSE_PATCH
|
|
if (!c || !ISVISIBLE(c))
|
|
c = getpointerclient();
|
|
#endif // FOCUSFOLLOWMOUSE_PATCH
|
|
#if STICKY_PATCH
|
|
if (!c || !ISVISIBLE(c))
|
|
for (c = selmon->stack; c && (!ISVISIBLE(c) || c->issticky); c = c->snext);
|
|
#endif // STICKY_PATCH
|
|
if (!c || !ISVISIBLE(c))
|
|
for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
|
|
if (selmon->sel && selmon->sel != c)
|
|
unfocus(selmon->sel, 0, c);
|
|
if (c) {
|
|
if (c->mon != selmon)
|
|
selmon = c->mon;
|
|
if (c->isurgent)
|
|
seturgent(c, 0);
|
|
detachstack(c);
|
|
attachstack(c);
|
|
grabbuttons(c, 1);
|
|
#if !BAR_FLEXWINTITLE_PATCH
|
|
#if RENAMED_SCRATCHPADS_PATCH
|
|
if (c->scratchkey != 0 && c->isfloating)
|
|
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColFloat].pixel);
|
|
else if (c->scratchkey != 0)
|
|
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel);
|
|
else if (c->isfloating)
|
|
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
|
|
else
|
|
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
|
|
#else
|
|
if (c->isfloating)
|
|
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
|
|
else
|
|
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
|
|
#endif // RENAMED_SCRATCHPADS_PATCH
|
|
#endif // BAR_FLEXWINTITLE_PATCH
|
|
setfocus(c);
|
|
} else {
|
|
#if NODMENU_PATCH
|
|
XSetInputFocus(dpy, selmon->bar && selmon->bar->win ? selmon->bar->win : root, RevertToPointerRoot, CurrentTime);
|
|
#else
|
|
XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
|
|
#endif // NODMENU_PATCH
|
|
XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
|
|
}
|
|
selmon->sel = c;
|
|
drawbars();
|
|
|
|
#if ON_EMPTY_KEYS_PATCH
|
|
if ((isempty && selmon->sel) || (!isempty && !selmon->sel)) {
|
|
isempty = !isempty;
|
|
grabkeys();
|
|
}
|
|
#endif // ON_EMPTY_KEYS_PATCH
|
|
}
|
|
|
|
/* there are some broken focus acquiring clients needing extra handling */
|
|
void
|
|
focusin(XEvent *e)
|
|
{
|
|
XFocusChangeEvent *ev = &e->xfocus;
|
|
|
|
if (selmon->sel && ev->window != selmon->sel->win)
|
|
setfocus(selmon->sel);
|
|
}
|
|
|
|
void
|
|
focusmon(const Arg *arg)
|
|
{
|
|
Monitor *m;
|
|
#if LOSEFULLSCREEN_PATCH
|
|
Client *sel;
|
|
#endif // LOSEFULLSCREEN_PATCH
|
|
|
|
if (!mons->next)
|
|
return;
|
|
if ((m = dirtomon(arg->i)) == selmon)
|
|
return;
|
|
#if LOSEFULLSCREEN_PATCH
|
|
sel = selmon->sel;
|
|
selmon = m;
|
|
unfocus(sel, 0, NULL);
|
|
#else
|
|
unfocus(selmon->sel, 0, NULL);
|
|
selmon = m;
|
|
#endif // LOSEFULLSCREEN_PATCH
|
|
focus(NULL);
|
|
#if WARP_PATCH
|
|
warp(selmon->sel);
|
|
#endif // WARP_PATCH
|
|
}
|
|
|
|
#if !STACKER_PATCH
|
|
void
|
|
focusstack(const Arg *arg)
|
|
{
|
|
Client *c = NULL, *i;
|
|
|
|
#if LOSEFULLSCREEN_PATCH
|
|
if (!selmon->sel)
|
|
return;
|
|
#elif FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
|
|
if (!selmon->sel || (selmon->sel->isfullscreen && !selmon->sel->fakefullscreen))
|
|
return;
|
|
#else
|
|
if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
|
|
return;
|
|
#endif // LOSEFULLSCREEN_PATCH
|
|
#if BAR_WINTITLEACTIONS_PATCH
|
|
if (arg->i > 0) {
|
|
for (c = selmon->sel->next; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next);
|
|
if (!c)
|
|
for (c = selmon->clients; c && (!ISVISIBLE(c) || (arg->i == 1 && HIDDEN(c))); c = c->next);
|
|
} else {
|
|
for (i = selmon->clients; i != selmon->sel; i = i->next)
|
|
if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i)))
|
|
c = i;
|
|
if (!c)
|
|
for (; i; i = i->next)
|
|
if (ISVISIBLE(i) && !(arg->i == -1 && HIDDEN(i)))
|
|
c = i;
|
|
}
|
|
#else
|
|
if (arg->i > 0) {
|
|
for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
|
|
if (!c)
|
|
for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
|
|
} else {
|
|
for (i = selmon->clients; i != selmon->sel; i = i->next)
|
|
if (ISVISIBLE(i))
|
|
c = i;
|
|
if (!c)
|
|
for (; i; i = i->next)
|
|
if (ISVISIBLE(i))
|
|
c = i;
|
|
}
|
|
#endif // BAR_WINTITLEACTIONS_PATCH
|
|
if (c) {
|
|
focus(c);
|
|
restack(selmon);
|
|
}
|
|
}
|
|
#endif // STACKER_PATCH
|
|
|
|
Atom
|
|
getatomprop(Client *c, Atom prop, Atom req)
|
|
{
|
|
int di;
|
|
unsigned long dl;
|
|
unsigned char *p = NULL;
|
|
Atom da, atom = None;
|
|
|
|
#if BAR_SYSTRAY_PATCH
|
|
if (prop == xatom[XembedInfo])
|
|
req = xatom[XembedInfo];
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
|
|
/* FIXME getatomprop should return the number of items and a pointer to
|
|
* the stored data instead of this workaround */
|
|
if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
|
|
&da, &di, &dl, &dl, &p) == Success && p) {
|
|
atom = *(Atom *)p;
|
|
#if BAR_SYSTRAY_PATCH
|
|
if (da == xatom[XembedInfo] && dl == 2)
|
|
atom = ((Atom *)p)[1];
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
XFree(p);
|
|
}
|
|
return atom;
|
|
}
|
|
|
|
int
|
|
getrootptr(int *x, int *y)
|
|
{
|
|
int di;
|
|
unsigned int dui;
|
|
Window dummy;
|
|
|
|
return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
|
|
}
|
|
|
|
long
|
|
getstate(Window w)
|
|
{
|
|
int format;
|
|
long result = -1;
|
|
unsigned char *p = NULL;
|
|
unsigned long n, extra;
|
|
Atom real;
|
|
|
|
if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
|
|
&real, &format, &n, &extra, (unsigned char **)&p) != Success)
|
|
return -1;
|
|
if (n != 0)
|
|
result = *p;
|
|
XFree(p);
|
|
return result;
|
|
}
|
|
|
|
int
|
|
gettextprop(Window w, Atom atom, char *text, unsigned int size)
|
|
{
|
|
char **list = NULL;
|
|
int n;
|
|
XTextProperty name;
|
|
|
|
if (!text || size == 0)
|
|
return 0;
|
|
text[0] = '\0';
|
|
if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
|
|
return 0;
|
|
if (name.encoding == XA_STRING) {
|
|
strncpy(text, (char *)name.value, size - 1);
|
|
} else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
|
|
strncpy(text, *list, size - 1);
|
|
XFreeStringList(list);
|
|
}
|
|
text[size - 1] = '\0';
|
|
XFree(name.value);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
grabbuttons(Client *c, int focused)
|
|
{
|
|
updatenumlockmask();
|
|
{
|
|
unsigned int i, j;
|
|
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
|
|
XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
|
|
if (!focused)
|
|
XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
|
|
BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
|
|
for (i = 0; i < LENGTH(buttons); i++)
|
|
if (buttons[i].click == ClkClientWin
|
|
#if NO_MOD_BUTTONS_PATCH
|
|
&& (nomodbuttons || buttons[i].mask != 0)
|
|
#endif // NO_MOD_BUTTONS_PATCH
|
|
)
|
|
for (j = 0; j < LENGTH(modifiers); j++)
|
|
XGrabButton(dpy, buttons[i].button,
|
|
buttons[i].mask | modifiers[j],
|
|
c->win, False, BUTTONMASK,
|
|
GrabModeAsync, GrabModeSync, None, None);
|
|
}
|
|
}
|
|
|
|
void
|
|
#if KEYMODES_PATCH
|
|
grabdefkeys(void)
|
|
#else
|
|
grabkeys(void)
|
|
#endif // KEYMODES_PATCH
|
|
{
|
|
updatenumlockmask();
|
|
{
|
|
unsigned int i, j, k;
|
|
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
|
|
int start, end, skip;
|
|
KeySym *syms;
|
|
|
|
XUngrabKey(dpy, AnyKey, AnyModifier, root);
|
|
XDisplayKeycodes(dpy, &start, &end);
|
|
syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
|
|
if (!syms)
|
|
return;
|
|
for (k = start; k <= end; k++)
|
|
for (i = 0; i < LENGTH(keys); i++)
|
|
/* skip modifier codes, we do that ourselves */
|
|
if (keys[i].keysym == syms[(k - start) * skip])
|
|
for (j = 0; j < LENGTH(modifiers); j++)
|
|
XGrabKey(dpy, k,
|
|
keys[i].mod | modifiers[j],
|
|
root, True,
|
|
GrabModeAsync, GrabModeAsync);
|
|
#if ON_EMPTY_KEYS_PATCH
|
|
if (!selmon->sel)
|
|
for (k = start; k <= end; k++)
|
|
for (i = 0; i < LENGTH(on_empty_keys); i++)
|
|
/* skip modifier codes, we do that ourselves */
|
|
if (on_empty_keys[i].keysym == syms[(k - start) * skip])
|
|
for (j = 0; j < LENGTH(modifiers); j++)
|
|
XGrabKey(dpy, k,
|
|
on_empty_keys[i].mod | modifiers[j],
|
|
root, True,
|
|
GrabModeAsync, GrabModeAsync);
|
|
#endif // ON_EMPTY_KEYS_PATCH
|
|
XFree(syms);
|
|
}
|
|
}
|
|
|
|
void
|
|
incnmaster(const Arg *arg)
|
|
{
|
|
#if PERTAG_PATCH
|
|
selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
|
|
#else
|
|
selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
|
|
#endif // PERTAG_PATCH
|
|
arrange(selmon);
|
|
}
|
|
|
|
#ifdef XINERAMA
|
|
static int
|
|
isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
|
|
{
|
|
while (n--)
|
|
if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
|
|
&& unique[n].width == info->width && unique[n].height == info->height)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
#endif /* XINERAMA */
|
|
|
|
void
|
|
#if KEYMODES_PATCH
|
|
keydefpress(XEvent *e)
|
|
#else
|
|
keypress(XEvent *e)
|
|
#endif // KEYMODES_PATCH
|
|
{
|
|
unsigned int i;
|
|
int keysyms_return;
|
|
KeySym* keysym;
|
|
XKeyEvent *ev;
|
|
|
|
ev = &e->xkey;
|
|
keysym = XGetKeyboardMapping(dpy, (KeyCode)ev->keycode, 1, &keysyms_return);
|
|
for (i = 0; i < LENGTH(keys); i++)
|
|
if (*keysym == keys[i].keysym
|
|
&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
|
|
&& keys[i].func)
|
|
keys[i].func(&(keys[i].arg));
|
|
#if ON_EMPTY_KEYS_PATCH
|
|
if (!selmon->sel)
|
|
for (i = 0; i < LENGTH(on_empty_keys); i++)
|
|
if (*keysym == on_empty_keys[i].keysym
|
|
&& CLEANMASK(on_empty_keys[i].mod) == CLEANMASK(ev->state)
|
|
&& on_empty_keys[i].func)
|
|
on_empty_keys[i].func(&(on_empty_keys[i].arg));
|
|
#endif // ON_EMPTY_KEYS_PATCH
|
|
XFree(keysym);
|
|
}
|
|
|
|
void
|
|
killclient(const Arg *arg)
|
|
{
|
|
#if ISPERMANENT_PATCH
|
|
if (!selmon->sel || selmon->sel->ispermanent)
|
|
#else
|
|
if (!selmon->sel)
|
|
#endif // ISPERMANENT_PATCH
|
|
return;
|
|
#if BAR_SYSTRAY_PATCH
|
|
if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0))
|
|
#else
|
|
if (!sendevent(selmon->sel, wmatom[WMDelete]))
|
|
#endif // BAR_SYSTRAY_PATCH
|
|
{
|
|
XGrabServer(dpy);
|
|
XSetErrorHandler(xerrordummy);
|
|
XSetCloseDownMode(dpy, DestroyAll);
|
|
XKillClient(dpy, selmon->sel->win);
|
|
XSync(dpy, False);
|
|
XSetErrorHandler(xerror);
|
|
XUngrabServer(dpy);
|
|
#if WARP_PATCH
|
|
force_warp = 1;
|
|
#endif // WARP_PATCH
|
|
}
|
|
#if SWAPFOCUS_PATCH && PERTAG_PATCH
|
|
selmon->pertag->prevclient[selmon->pertag->curtag] = NULL;
|
|
#endif // SWAPFOCUS_PATCH
|
|
}
|
|
|
|
void
|
|
manage(Window w, XWindowAttributes *wa)
|
|
{
|
|
Client *c, *t = NULL;
|
|
#if SWALLOW_PATCH
|
|
Client *term = NULL;
|
|
#endif // SWALLOW_PATCH
|
|
#if SEAMLESS_RESTART_PATCH
|
|
int settings_restored;
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
Window trans = None;
|
|
XWindowChanges wc;
|
|
|
|
c = ecalloc(1, sizeof(Client));
|
|
c->win = w;
|
|
#if SWALLOW_PATCH
|
|
c->pid = winpid(w);
|
|
#endif // SWALLOW_PATCH
|
|
/* geometry */
|
|
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
|
|
c->sfx = c->sfy = c->sfw = c->sfh = -9999;
|
|
#endif // SAVEFLOATS_PATCH | EXRESIZE_PATCH
|
|
c->x = c->oldx = wa->x;
|
|
c->y = c->oldy = wa->y;
|
|
c->w = c->oldw = wa->width;
|
|
c->h = c->oldh = wa->height;
|
|
c->oldbw = wa->border_width;
|
|
#if CFACTS_PATCH
|
|
c->cfact = 1.0;
|
|
#endif // CFACTS_PATCH
|
|
#if SEAMLESS_RESTART_PATCH
|
|
settings_restored = restoreclientstate(c);
|
|
#endif // SEAMLESS_RESTART_PATCH
|
|
#if BAR_WINICON_PATCH
|
|
updateicon(c);
|
|
#endif // BAR_WINICON_PATCH
|
|
updatetitle(c);
|
|
|
|
#if XKB_PATCH
|
|
/* Setting current xkb state must be before applyrules */
|
|
if (!(c->xkb = findxkb(c->win)))
|
|
c->xkb = createxkb(c->win);
|
|
#endif // XKB_PATCH
|
|
|
|
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
|
|
c->mon = t->mon;
|
|
c->tags = t->tags;
|
|
#if SETBORDERPX_PATCH
|
|
c->bw = c->mon->borderpx;
|
|
#else
|
|
c->bw = borderpx;
|
|
#endif // SETBORDERPX_PATCH
|
|
#if CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH
|
|
c->x = t->x + WIDTH(t) / 2 - WIDTH(c) / 2;
|
|
c->y = t->y + HEIGHT(t) / 2 - HEIGHT(c) / 2;
|
|
#elif CENTER_PATCH && CENTER_TRANSIENT_WINDOWS_PATCH
|
|
c->iscentered = 1;
|
|
#elif CENTER_TRANSIENT_WINDOWS_PATCH
|
|
c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2;
|
|