Terminal

如何創建一個不會通過終端命令以外的任何方式關閉的 X 視窗

  • June 11, 2021

我正在嘗試創建一個從 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其他終端視窗(不太喜歡它,但也許可以接受)。

有任何想法嗎?

如果切換到xterm是一個選項,您可以使用下面的 hack。不過有一些注意事項。一旦解決了其中的大多數問題,解決方案就會變得非常複雜,請參閱最後的腳本。

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

在收到來自視窗管理器的關閉指令後,xterm將向您的應用程序的程序組發送一個 SIGHUP 信號,並且僅在程序返回時自行退出。

這假設您的應用程序不會重置 SIGHUP 的處理程序,並且可能會對您的應用程序的子級產生不必要的副作用。

如果您的應用程序是tmux.

要解決這些問題,您可以執行以下操作:

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

這樣,tmux將在不同的程序組中啟動,因此只有shSIGHUP 會收到(並忽略它)。

現在,這不適用於tmux重置這些信號的處理程序,但在一般情況下,根據您的實現sh,SIGINT、SIGQUIT 信號以及通常兩者都將被您的應用程序忽略,因為它bash作為啟動來自非互動式的非同步命令sh。這意味著您不能使用Ctrl+Cor中斷您的應用程序Ctrl+\

這是 POSIX 要求。一些 shell 像mksh不尊重它(至少不是目前版本),或者只是部分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,對於某些實現,如mksh,yashrc,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;
 ' "$@"

引用自:https://unix.stackexchange.com/questions/270664