From 42889d9c0043c4c6cf74815ff9e92209bb896b03 Mon Sep 17 00:00:00 2001 From: Andrey Rodionov Date: Sat, 24 Mar 2018 00:34:13 +0300 Subject: [PATCH] Add a group of programs by window type (issue #453) --- jwm.1.in | 10 ++++++++-- src/group.c | 37 ++++++++++++++++++++++++++++++++++++- src/group.h | 6 ++++++ src/hint.c | 15 ++++++++++++--- src/hint.h | 14 ++++++++++++++ src/lex.c | 1 + src/lex.h | 1 + src/parse.c | 3 +++ 8 files changed, 81 insertions(+), 6 deletions(-) diff --git a/jwm.1.in b/jwm.1.in index 020a5ad9..047fa2b0 100644 --- a/jwm.1.in +++ b/jwm.1.in @@ -749,8 +749,8 @@ output is the same as the main configuration file. .B "GROUP SETTINGS" .RS Program groups allow one to specify options which apply to a group of -programs by name and/or class. A program group is created with the -\fBGroup\fP tag. As many program groups can be created as desired. +programs by name, class and window type. A program group is created with +the \fBGroup\fP tag. As many program groups can be created as desired. If one or more \fBName\fP tags is specified, at least one name must match. Likewise, if one or more \fBClass\fP tags is specified, at least one class must match. @@ -768,6 +768,12 @@ first string in WM_CLASS). The window class for a program to match to be in this group (the second string in WM_CLASS). .RE +.B Type +.RS +The window type for a program to match to be in this group. Possible +values are desktop, dialog, dock, menu, normal, notification, splash, +toolbar, utility. +.RE .B Option .RS An option for this group. Possible options are: diff --git a/src/group.c b/src/group.c index eaf5f211..6900de76 100644 --- a/src/group.c +++ b/src/group.c @@ -20,6 +20,7 @@ typedef unsigned int MatchType; #define MATCH_NAME 0 /**< Match the window name. */ #define MATCH_CLASS 1 /**< Match the window class. */ +#define MATCH_TYPE 2 /**< Match the window type. */ /** List of match patterns for a group. */ typedef struct PatternListType { @@ -127,6 +128,17 @@ void AddGroupName(GroupType *gp, const char *pattern) } } +/** Add a window type to a group. */ +void AddGroupType(GroupType *gp, const char *pattern) +{ + Assert(gp); + if(JLIKELY(pattern)) { + AddPattern(&gp->patterns, pattern, MATCH_TYPE); + } else { + Warning(_("invalid group type")); + } +} + /** Add a pattern to a pattern list. */ void AddPattern(PatternListType **lp, const char *pattern, MatchType match) { @@ -199,15 +211,31 @@ void ApplyGroups(ClientNode *np) GroupType *gp; char hasClass; char hasName; + char hasType; char matchesClass; char matchesName; + char matchesType; + + static const StringMappingType windowTypeMapping[] = { + { "desktop", WINDOW_TYPE_DESKTOP }, + { "dialog", WINDOW_TYPE_DIALOG }, + { "dock", WINDOW_TYPE_DOCK }, + { "menu", WINDOW_TYPE_MENU }, + { "normal", WINDOW_TYPE_NORMAL }, + { "notification", WINDOW_TYPE_NOTIFICATION }, + { "splash", WINDOW_TYPE_SPLASH }, + { "toolbar", WINDOW_TYPE_TOOLBAR }, + { "utility", WINDOW_TYPE_UTILITY } + }; Assert(np); for(gp = groups; gp; gp = gp->next) { hasClass = 0; hasName = 0; + hasType = 0; matchesClass = 0; matchesName = 0; + matchesType = 0; for(lp = gp->patterns; lp; lp = lp->next) { if(lp->match == MATCH_CLASS) { if(Match(lp->pattern, np->className)) { @@ -219,11 +247,18 @@ void ApplyGroups(ClientNode *np) matchesName = 1; } hasName = 1; + } else if(lp->match == MATCH_TYPE) { + if(FindValue(windowTypeMapping, WINDOW_TYPE_COUNT, lp->pattern) + == np->state.windowType) { + matchesType = 1; + } + hasType = 1; } else { Debug("invalid match in ApplyGroups: %d", lp->match); } } - if(hasName == matchesName && hasClass == matchesClass) { + if(hasName == matchesName && hasClass == matchesClass + && hasType == matchesType) { ApplyGroup(gp, np); } } diff --git a/src/group.h b/src/group.h index 8ea89f19..1e5f1eaf 100644 --- a/src/group.h +++ b/src/group.h @@ -82,6 +82,12 @@ void AddGroupClass(struct GroupType *gp, const char *pattern); */ void AddGroupName(struct GroupType *gp, const char *pattern); +/** Add a window type to a group. + * @param gp The group. + * @param pattern A pattern to match with the window type. + */ +void AddGroupType(struct GroupType *gp, const char *pattern); + /** Add a group option that doesn't take a value. * @param gp The group. * @param option The option. diff --git a/src/hint.c b/src/hint.c index 1df3d545..7e8f90d6 100644 --- a/src/hint.c +++ b/src/hint.c @@ -639,6 +639,7 @@ ClientState ReadWindowState(Window win, char alreadyMapped) state = (Atom*)temp; for(x = 0; x < count; x++) { if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_NORMAL]) { + result.windowType = WINDOW_TYPE_NORMAL; break; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_DESKTOP]) { result.defaultLayer = LAYER_DESKTOP; @@ -646,35 +647,43 @@ ClientState ReadWindowState(Window win, char alreadyMapped) result.status |= STAT_STICKY; result.status |= STAT_NOLIST; result.status |= STAT_NOFOCUS; + result.windowType = WINDOW_TYPE_DESKTOP; break; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_DOCK]) { result.border = BORDER_NONE; result.defaultLayer = LAYER_ABOVE; result.status |= STAT_NOFOCUS; + result.windowType = WINDOW_TYPE_DOCK; break; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_SPLASH]) { - result.border = BORDER_NONE; + result.border = BORDER_NONE; + result.windowType = WINDOW_TYPE_SPLASH; break; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_DIALOG]) { - result.border &= ~BORDER_MIN; - result.border &= ~BORDER_MAX; + result.border &= ~BORDER_MIN; + result.border &= ~BORDER_MAX; + result.windowType = WINDOW_TYPE_DIALOG; break; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_MENU]) { result.border &= ~BORDER_MAX; result.status |= STAT_NOLIST; + result.windowType = WINDOW_TYPE_MENU; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION]) { result.border = BORDER_NONE; result.status |= STAT_NOLIST; result.status |= STAT_NOFOCUS; + result.windowType = WINDOW_TYPE_NOTIFICATION; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_TOOLBAR]) { result.border &= ~BORDER_MAX; result.defaultLayer = LAYER_ABOVE; result.status |= STAT_STICKY; result.status |= STAT_NOLIST; result.status |= STAT_NOFOCUS; + result.windowType = WINDOW_TYPE_TOOLBAR; } else if( state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_UTILITY]) { result.border &= ~BORDER_MAX; result.status |= STAT_NOFOCUS; + result.windowType = WINDOW_TYPE_UTILITY; } else { Debug("Unknown _NET_WM_WINDOW_TYPE: %lu", state[x]); } diff --git a/src/hint.h b/src/hint.h index 016b504d..8c15ecc2 100644 --- a/src/hint.h +++ b/src/hint.h @@ -145,6 +145,19 @@ typedef unsigned char WinLayerType; #define LAST_LAYER LAYER_ABOVE #define DEFAULT_TRAY_LAYER LAYER_ABOVE +/** Enumeration of window type. */ +typedef unsigned char WindowType; +#define WINDOW_TYPE_DESKTOP 0 +#define WINDOW_TYPE_DOCK 1 +#define WINDOW_TYPE_SPLASH 2 +#define WINDOW_TYPE_DIALOG 3 +#define WINDOW_TYPE_NORMAL 4 +#define WINDOW_TYPE_MENU 5 +#define WINDOW_TYPE_NOTIFICATION 6 +#define WINDOW_TYPE_TOOLBAR 7 +#define WINDOW_TYPE_UTILITY 8 +#define WINDOW_TYPE_COUNT 9 + /** Client state information. */ typedef struct ClientState { unsigned int status; /**< Status bit mask. */ @@ -154,6 +167,7 @@ typedef struct ClientState { unsigned char maxFlags; /**< Maximization status. */ unsigned char layer; /**< Current window layer. */ unsigned char defaultLayer; /**< Default window layer. */ + unsigned char windowType; /**< Window type. */ } ClientState; extern Atom atoms[ATOM_COUNT]; diff --git a/src/lex.c b/src/lex.c index 5d01032d..e30e39bc 100644 --- a/src/lex.c +++ b/src/lex.c @@ -89,6 +89,7 @@ static const StringMappingType TOKEN_MAP[] = { { "TrayButton", TOK_TRAYBUTTON }, { "TrayButtonStyle", TOK_TRAYBUTTONSTYLE }, { "TrayStyle", TOK_TRAYSTYLE }, + { "Type", TOK_TYPE }, { "Width", TOK_WIDTH }, { "WindowStyle", TOK_WINDOWSTYLE } }; diff --git a/src/lex.h b/src/lex.h index a73db1ef..3c9aa4a7 100644 --- a/src/lex.h +++ b/src/lex.h @@ -85,6 +85,7 @@ typedef enum { TOK_TRAYBUTTON, TOK_TRAYBUTTONSTYLE, TOK_TRAYSTYLE, + TOK_TYPE, TOK_WIDTH, TOK_WINDOWSTYLE diff --git a/src/parse.c b/src/parse.c index c3055c19..721aad18 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1810,6 +1810,9 @@ void ParseGroup(const TokenNode *tp) case TOK_NAME: AddGroupName(group, np->value); break; + case TOK_TYPE: + AddGroupType(group, np->value); + break; case TOK_OPTION: ParseGroupOption(np, group, np->value); break;