如何创建一个 X 窗口,该窗口不会通过终端命令以外的任何方式关闭

如何创建一个 X 窗口,该窗口不会通过终端命令以外的任何方式关闭

我正在尝试创建一个从 X11 开始的进程,该进程将是图形化的并且无法被我自己的用户杀死。

更具体地说,我有一个 urxvt 下拉终端,在启动时启动并立即运行 tmux 会话。让我烦恼的是,当我忘记它是下拉终端而我应该隐藏它时,有时我会用Alt+杀死它。F4

当然,我不会丢失 tmux 会话,因为我可以从另一个终端重新连接到它,但我必须重新启动下拉终端(使用确切的配置)并重新连接,这很乏味。

我想做的是无法Alt杀死F4 这个特定进程

读完这篇文章后: 使 Linux 上的进程不可终止

我知道我应该以属于另一个用户的身份启动该进程,但即使我设法在我自己的 X 服务器中执行此操作:

    sudo xhost +local:
    sudo runuser -l my-secondary-user -c urxvt

我面临两种不良行为:

  1. 该进程可由我的主用户(拥有 X 会话的用户)终止。
  2. 虚拟终端开始以 my-secondary-user 身份登录(当然)。

(2) 可能是可以纠正的 - 只需运行命令以我的主用户身份登录 - 但我不知道如何修复 (1)。

有没有办法让这项工作正常进行,或者有其他方法来做我想做的事?如果可能的话,我宁愿避免创建内核模块。

使用 Openbox 运行 Arch Linux。

编辑

我想我也许可以让Alt+F4不适用于这个特定的窗口。发现了这个: Openbox:根据每个应用程序禁用 Alt-F4

并修改了我的rc.xml

    <keybind key="A-F4">
     <action name="If">
      <application class="URxvt"
        title="tmux-session">
      </application>
      <then><!-- Do nothing for urxvt-tmux-session --></then>
      <else>
       <action name="Close" />
      </else>
     </action>
    </keybind>

这比以前稍微好一些:我不能Alt+F4那个终端,但现在也不能Alt+F4任何 urxvt 窗口。 (猜猜这是因为新的 urxvt 窗口都在相同的类名下运行?)

除此之外,我可以从菜单栏中删除我需要的所有内容,或者右键单击我的色调面板,或者当然,通过命令。但这种方法意味着:

  1. 我仍然可以通过右键单击 Tint2 面板来终止我的下拉终端(如果我使下拉终端不作为图标出现在面板上,也许可以修复 - 通过一些研究应该可以实现)。
  2. 我不能Alt+F4其他终端窗口(不太喜欢它,但也许可以接受)。

有任何想法吗?

答案1

如果xterm可以选择切换到,您可以使用下面的技巧。但有一些注意事项。一旦解决了其中的大多数问题,解决方案就会变得相当复杂,请参阅最后的最终脚本。

xterm -e 'trap "" HUP; your-application'

在收到窗口管理器的关闭指令后,xterm将向您的应用程序的进程组发送 SIGHUP,并且仅在进程返回时自行退出。

假设您的应用程序不会重置 SIGHUP 的处理程序,并且可能会对您的应用程序的子应用程序产生不必要的副作用。

如果您的应用程序是tmux.

要解决这些问题,您可以这样做:

xterm -e sh -c 'bash -mc tmux <&1 & trap "" HUP; wait'

这样,tmux将在不同的进程组中启动,因此只有sh会收到 SIGHUP (并忽略它)。

现在,这并不适用于tmux重置这些信号的处理程序,但在一般情况下,根据您的实现sh,SIGINT、SIGQUIT 信号通常都会被您的应用程序忽略,因为它bash是作为来自非交互式sh.这意味着您无法使用Ctrl+C或中断您的应用程序Ctrl+\

这是 POSIX 的要求。有些 shellmksh不支持它(至少当前版本不支持),或者仅部分dash支持 SIGINT 但不支持 SIGQUIT。因此,如果mksh可用,您可以这样做:

xterm -e mksh -c 'bash -mc your-application <&1 & trap "" HUP; wait'

mksh(尽管如果他们决定修复该不符合项,这可能在未来版本中不起作用)。

或者,如果您不能保证mkshbash将可用,或者不想依赖将来可能改变的行为,您可以手动完成他们的工作,perl例如编写一个unclosable-xterm包装脚本,如下所示:

#! /bin/sh -
[ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}"
exec xterm -e perl -MPOSIX -e '
  $pid = fork;
  if ($pid == 0) {
    setpgrp or die "setpgrp: $!";
    tcsetpgrp(0,getpid) or die "tcsetpgrp: $!";
    exec @ARGV;
    die "exec: $!";
  }
  die "fork: $!" if $pid < 0;
  $SIG{HUP} = "IGNORE";
  waitpid(-1,WUNTRACED)' "$@"

(称为unclosable-xterm your-application and its args)。

现在,另一个副作用是我们正在创建并放入前台的新进程组(上面带有bash -msetpgrp+ tcsetpgrp)不再是会话领导者进程组,因此不再是成为孤儿的进程组(据说现在有一个父进程在照顾它(shperl))。

这意味着按下 后Ctrl+Z,该过程将被暂停。在这里,我们粗心的父进程将退出,这意味着进程组将收到 SIGHUP (并希望死亡)。

为了避免这种情况,我们可以忽略子进程中的 SIGTSTP,但是如果您的应用程序是交互式 shell,则对于某些实现(例如mkshyashrc),Ctrl-Z将不适用于它们运行的​​作业。

或者我们可以实现一个更仔细的父级,每次停止时都会恢复子级,例如:

#! /bin/sh -
[ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}"
exec xterm -e perl -MPOSIX -e '
  $pid = fork;
  if ($pid == 0) {
    setpgrp or die "setpgrp: $!";
    tcsetpgrp(0,getpid) or die "tcsetpgrp: $!";
    exec @ARGV;
    die "exec: $!";
  }
  die "fork: $!" if $pid < 0;
  $SIG{HUP} = "IGNORE";
  while (waitpid(-1,WUNTRACED) > 0 && WIFSTOPPED(${^CHILD_ERROR_NATIVE})) {
    kill "CONT", -$pid;
  }' "$@"

另一个问题是,如果xterm由于其他原因而消失关闭从窗口管理器,例如,如果xterm被终止或失去与 X 服务器的连接(因为xkill破坏例如,您的窗口管理器的操作,或者 X 服务器崩溃),那么这些进程不会终止,因为在这些情况下也会使用 SIGHUP 来终止它们。要解决这个问题,您可以poll()在终端设备上使用(终端设备运行时会被拆除xterm):

#! /bin/sh -
[ "$#" -gt 0 ] || set -- "${SHELL:-/bin/sh}"
exec xterm -e perl -w -MPOSIX -MIO::Poll -e '
  $pid = fork; # start the command in a child process
  if ($pid == 0) {
    setpgrp or die "setpgrp: $!"; # new process group
    tcsetpgrp(0,getpid) or die "tcsetpgrp: $!"; # in foreground
    exec @ARGV;
    die "exec: $!";
  }
  die "fork: $!" if $pid < 0;
  $SIG{HUP} = "IGNORE"; # ignore SIGHUP in the parent
  $SIG{CHLD} = sub {
    if (waitpid(-1,WUNTRACED) == $pid) {
      if (WIFSTOPPED(${^CHILD_ERROR_NATIVE})) {
        # resume the process when stopped
        # we may want to do that only for SIGTSTP though
        kill "CONT", -$pid;
      } else {
        # exit when the process dies
        exit;
      }
    }
  };

  # watch for terminal hang-up
  $p = IO::Poll->new;
  $p->mask(STDIN, POLLERR);
  while ($p->poll <= 0 || $p->events(STDIN) & POLLHUP == 0) {};
  kill "HUP", -$pid;
  ' "$@"

答案2

你的问题有一个有点误导性的标题,导致你的研究走向错误的方向。当您关闭窗口时X服务器不会终止你的进程。相反,它WM_DELETE_WINDOW向进程发送一条消息,进程随后终止。这个事实注定了你的第一种方法:即使你设置了标志SIGNAL_UNKILLABLE,你仍然能够关闭它。

您的第二种方法似乎可用,您所要做的就是区分单个终端窗口和所有其他窗口。实现此目的的一种方法是urxvt从特定标题(例如 )开始,urxvt -name urxvt-iddqd并为其名称创建规则:

<keybind key="A-F4">
 <action name="If">
  <application title="urxvt-iddqd"
    title="tmux-session">
  </application>
  <then><!-- Do nothing for urxvt-tmux-session --></then>
  <else>
   <action name="Close" />
  </else>
 </action>
</keybind>

答案3

问题的作者提供了以下解决方案:

我找到了一个适合我的解决方案德米特里的回答和我上面的编辑:

  1. 我为我的设置了一个自定义标题urxvt
urxvt -title urxvt-drop-down

我没有使用自定义名称,因为看起来

urxvt -name urxvt-drop-down

命令忽略.Xresources我当前使用的文件,而-title使用它。

  1. 我使用Alt+F4键绑定来忽略标题为 的终端urxvt-drop-down。 rc.xml:
   <keybind key="A-F4">
    <action name="If">
     <title>urxvt-drop-down</title>
     <then><!-- Do nothing for urxvt-drop-down --></then>
     <else>
      <action name="Close" />
     </else>
    </action>
   </keybind>
  1. 我让罪魁祸首永远不会出现在任务栏中,并且由于它skip_taskbar也使窗口失去焦点,因此重新聚焦它。 rc.xml:
   <application title="urxvt-drop-down">
     <skip_taskbar>yes</skip_taskbar>
     <focus>yes</focus>
   </application>

尽管这为我解决了问题,但这本质上是一种解决方法。因此我还不确定是否斯蒂芬的回答是这个问题的正确答案。

需要对所提供的命令进行一些研究以了解正在执行的操作。

不过,我不会使用xterm,因为我想要假透明度和下拉功能(我认为 xterm 也可能有某种下拉功能,但如果没有合成器,透明度肯定是不可能的 - 而且我不会使用合成器)。

相关内容