2

我正在使用dwm (6.2)窗口管理器,我发现了一个我很想解决的错误。

窗口管理器使用放置窗口的“主区域”“堆栈区域” :

在此处输入图像描述

可以使用+将“堆栈区域”顶部的窗口移动到“主区域”的底部。也可以使用+将窗口从“主区域”的底部移回“堆栈区域”的顶部。ALTiALTd

现在在这种情况下,如果我使用ALT+ ,布局会发生变化,并且在组合键之后, “主区域”i中有两个窗口:

在此处输入图像描述

我再重复一遍,现在“主区域”中有三个窗口:

在此处输入图像描述

我再重复一遍,现在“主区域”中有三个窗口,宽度为 100%:

在此处输入图像描述

如果此时我决定将窗口从“主区域”返回到“堆栈区域”,我将开始按ALT+d并且窗口将立即返回到“堆栈区域”。这工作正常。

但是我故意犯了一个错误,而是再次按ALT+ i,例如再按下。好像什么都没发生...

但是现在如果我尝试将窗口从“主区域”返回到“堆栈区域”,我首先需要再按ALT下+并且不会发生任何事情!最后,当我第四次按+时,窗口管理器会将第一个窗口从“主区域”底部返回到“堆栈区域”顶部。d ALTd

所以这没有经过深思熟虑,应该被认为是一个错误......


ALT源代码中必须有某种计数器,通过按+再增加三倍,但在所有窗口都已经在“主区域”i中之后它不应该增加。


config.def.h源文件 ( www ) 中有一部分代码分配了键。在这里我可以看到,当用户按下ALT+时,i函数incnmaster()被调用并传递了一个参数.i = +1 (我不明白这个参数)

static Key keys[] = {
    /* modifier                     key        function        argument */
    ...
    { MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
    { MODKEY,                       XK_d,      incnmaster,     {.i = -1 } },
    ... 
};

Keydwm.c源文件(www)内的结构:

typedef struct {
    unsigned int mod;
    KeySym keysym;
    void (*func)(const Arg *);
    const Arg arg;
} Key;

函数incnmaster()dwm.c源文件 ( www ) 中定义:

void
incnmaster(const Arg *arg)
{
    selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
    arrange(selmon);
} 

wherearg是指向Arg( Arg*) 的指针,它是一个联合(我不太明白如何处理参数.i = +1

 typedef union {
    int i;
    unsigned int ui;
    float f;
    const void *v;
 } Arg;

selmon是一个结构Monitor

struct Monitor {
    char ltsymbol[16];
    float mfact;
    int nmaster;
    int num;
    int by;               /* bar geometry */
    int mx, my, mw, mh;   /* screen size */
    int wx, wy, ww, wh;   /* window area  */
    unsigned int seltags;
    unsigned int sellt;
    unsigned int tagset[2];
    int showbar;
    int topbar;
    Client *clients;
    Client *sel;
    Client *stack;
    Monitor *next;
    Window barwin;
    const Layout *lt[2];
};

MAX在单独的源文件util.h( www ) 中定义为:

#define MAX(A, B)    ((A) > (B) ? (A) : (B))

和函数arrange()定义如下:

 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);
}

我觉得我不用再挖了...


现在我想我需要在 C 代码中实现某种if语句以防止selmon->nmaster增加太多,但我有点困惑。任何人都可以帮忙吗?

4

2 回答 2

1

当它是链表时,你为什么要持有客户数量?您无法按需获得客户数量。类似的代码可以在monocle count patch中找到。如果您真的必须自己保留该计数(出于性能原因),我会查看由 dwm 修改的客户列表的任何地方,并将该修改投射到计数器。

结构客户端包含指向“下一个”客户端的指针,实现可能取决于您何时想要使用多头支持,但使用类似于 Client* c = nexttiled(c->next) 的代码,其中第一个引用可以通过调用从 Monitor 获得客户端* c = nexttiled(monitor->clients)。如果你在循环中计算这些就足够了。

如果您仍然想自己计数,我会在 dwm.c 中找到与 Client 一起使用的函数(分离、附加、...),并找到哪些正在修改列表,您可以在其中根据执行的操作递增/递减计数器。

于 2020-04-21T15:16:46.690 回答
0

在我自己弄清楚这个之前,没有人回答。问题是 Suckless 团队从未实现过任何类型的机制来计算打开的窗口数量(他们称它们为客户端)。这就是我int nclients;向 struct 添加成员的原因Monitor

struct Monitor {
    char ltsymbol[16];
    float mfact;
    int nmaster;
    int nclients;
    int num;
    int by;               /* bar geometry */
    int mx, my, mw, mh;   /* screen size */
    int wx, wy, ww, wh;   /* window area  */
    unsigned int seltags;
    unsigned int sellt;
    unsigned int tagset[2];
    int showbar;
    int topbar;
    Client *clients;
    Client *sel;
    Client *stack;
    Monitor *next;
    Window barwin;
    const Layout *lt[2];
};

0然后我通过添加我猜在开始时运行的函数m->nclients = 0;来确保它在启动时被初始化:createmon()

Monitor *
createmon(void)
{
    Monitor *m;

    m = ecalloc(1, sizeof(Monitor));
    m->tagset[0] = m->tagset[1] = 1;
    m->mfact = mfact;
    m->nmaster = nmaster;
    m->nclients = 0;
    m->showbar = showbar;
    m->topbar = topbar;
    m->gappx = gappx;
    m->lt[0] = &layouts[0];
    m->lt[1] = &layouts[1 % LENGTH(layouts)];
    strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
    return m;
}

然后我确保nclients当新窗口出现时我的计数器增加。我在函数的开头添加了++selmon->nclients;and arrange(selmon); (以便能够在您关闭其中一个客户端后立即将客户端移动到堆栈/主控)语句:spawn()

void
spawn(const Arg *arg)
{
    ++selmon->nclients;
    arrange(selmon);
    if (arg->v == dmenucmd)
        dmenumon[0] = '0' + selmon->num;
    if (fork() == 0) {
        if (dpy)
            close(ConnectionNumber(dpy));
        setsid();
        execvp(((char **)arg->v)[0], (char **)arg->v);
        fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
        perror(" failed");
        exit(EXIT_SUCCESS);
    }
}

关闭窗口时应减少计数器。这就是为什么我在函数顶部添加了一个--selmon->nclients;arrange(selmon); (以便能够在您关闭其中一个客户端后立即将客户端移动到堆栈/主控) :killclient()

void
killclient(const Arg *arg)
{
    --selmon->nclients;
    arrange(selmon);
    if (!selmon->sel)
        return;
    if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
        XGrabServer(dpy);
        XSetErrorHandler(xerrordummy);
        XSetCloseDownMode(dpy, DestroyAll);
        XKillClient(dpy, selmon->sel->win);
        XSync(dpy, False);
        XSetErrorHandler(xerror);
        XUngrabServer(dpy);
    }
}

现在计数器已经设置好了,我可以用它来重写incnmaster()这样的函数:

void
incnmaster(const Arg *arg)
{
    if((arg->i > 0) && (selmon->nmaster < selmon->nclients)){
        ++selmon->nmaster;
    }
    if((arg->i < 0) && (selmon->nmaster > 0)){
        --selmon->nmaster;
    }
    arrange(selmon);

}

注意。我的 DWM 有点打补丁,所以有些行可能与你的有点不同,但只要坚持相同的理念,你就可以解决这个问题。

该解决方案部分有效。只有当我:

A、使用dmenu

dmenu启动时可以(a)打开客户端或(b)什么都不做。在情况(a)中,一切都按预期工作,但在情况(b) nmaster中,又nclients变得不同步。

因此,例如,如果我执行场景(b)一次并使用CTRL+i无限次,我将不得不使用CTRL+d一次并且什么都不会发生,但如果我再次使用它,一个窗口将从主窗口移动到堆栈区域。

B. 从终端运行任何类型的窗口应用程序

看起来 DWM 无法跟踪从终端运行的窗口并以错误的方式处理它们......在这种情况下也是如此,nmaster并且nclients变得不同步。


spawn有谁知道打开任何类型的窗口时是否还有其他功能?

这还没有解决!

于 2020-04-15T10:19:33.607 回答