我创建了一个名为“app”的 Firefox 配置文件,其中包含几个用于隐藏地址栏和标签栏的扩展。我的目标是获取 Google Inbox、日历等的 .desktop 文件,以模仿 Chrome 的“添加到桌面”功能。目前,我有类似这样的 .desktop 文件
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Google Inbox (Firefox)
Exec=firefox -P app -new-window --class googleinbox inbox.google.com
Icon=email
StartupWMClass=googleinbox
但是,当我有多个这样的窗口时,所有窗口都会在第一个窗口的图标下分组启动器。当我xprop WM_CLASS
在每个窗口上使用时,我发现它们确实都有我先打开的窗口的 WM_CLASS。
我怎样才能让 Firefox 处理-class
每个窗口而不是每个配置文件的标志?
编辑:我现在使用的/usr/bin/firefox -P "PROFILE NAME" --class=WMCLASS
是下面发布的 janky 脚本,只需要为每个 webapp 创建新的配置文件即可。此外,我还使用此用户Chrome.css删除 chrome 的地址和标签栏。
答案1
由于似乎没有人有针对 Firefox 的答案(可能是因为该-class
标志未记录在手册页中,无法正常工作),我只会发布我的丑陋黑客作为解决方案。如果有人可以大大改进它,我会给他们答案。
我编写了以下脚本,将其命名为 firefoxApp.sh,并将其放在 ~/bin/ 中。这是一个糟糕的黑客行为,我对此感到羞愧。但也引以为豪。我放弃了单独的 Firefox 配置文件,而是WM_CLASS
在延迟后仅使用 wmctrl、grep 和 xprop 来更改新创建的窗口的(因为窗口有时需要很长时间才能出现并显示标题)。如果有人能告诉我一种更精确、更可靠的方法来找到要重新分类的窗口,而且不受时间/竞争条件的困扰,那么这个脚本将会得到很大的改进。我尝试使用启动进程的 PID 来执行此操作,但失败了(可能是因为整个 Firefox 配置文件只有一个根 PID)。我不会在这里发布该尝试的代码,因为我不再拥有它。
#!/bin/sh
targetclass=$1
url=$2
titlegrep=$3
if [ "$#" -ne 3 ]
then
echo "USAGE: $0 TARGETCLASS URL TITLEGREP" 1>&2
exit 1
fi
firefox -P default -new-window "$url" &
sleep 10
# Ensure only newlines split items in the upcoming for loop:
IFS='
'
for wid in `wmctrl -l -x | grep $titlegrep | awk '{ print \$1 }'`
do
xprop -id $wid -f WM_CLASS 8s -set WM_CLASS $targetclass
done
我使用 ~/.local/share/applications 中的 .desktop 文件调用此脚本,如下所示。
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Calendar (Firefox)
Exec=firefoxApp.sh googlecalendar calendar.google.com Calendar
Icon=calendar
StartupWMClass=googlecalendar
如果我愿意,我可以使用更具体的图标名称google-calendar-firefox-app
,例如,然后放置一个名为google-calendar-firefox-app.svg
~/.local/share/icons 的文件。
更新:我决定为每个这样的“应用程序”使用实际的单独配置文件。设置它们需要多做一些工作,但这可以通过另一个脚本在很大程度上实现自动化我这里就不多说了,它可以与 Firefox 配合使用,而不是围绕它,从而消除了窗口重命名方法的竞争条件。
当然,这种方法的一个显著缺点是这些应用程序与主 Firefox 配置文件隔离,不会与其共享任何插件或扩展。因此,例如,您可能需要手动从 LastPass 复制密码。
作为奖励,该脚本还会填充一个userChrome.css
文件以隐藏新配置文件中的窗口镶边,这有助于创建 Web“应用程序”的幻觉。可以使用以下命令调用它以--help
获取
usage: create_firefox_app.py [-h] [--app_name APP_NAME]
[--hide_user_chrome HIDE_USER_CHROME]
[--run_after_creating RUN_AFTER_CREATING]
URL icon_name
positional arguments:
URL Homepage to be used when opening new windows in the
profile via the .desktop file.
icon_name Icon name to use in .desktop file. An explicit path
can be given, or something that resolves using the
regular icon search path (however that works; e.g.,
for ~/.local/share/icons/gmail.svg, you could enter
just gmail.svg).
optional arguments:
-h, --help show this help message and exit
--app_name APP_NAME Name for the generated "app" to use in the .desktop
file. If not given, a santized version of the URL will
be used instead. (default: None)
--hide_user_chrome HIDE_USER_CHROME
Whether to generate userChrome.css file that will hide
window chrome in all windows created in the new
profile. Useful to make web-apps seem more app-y.
(default: True)
--run_after_creating RUN_AFTER_CREATING
Whether we should start the new app after creating it.
(default: True)
如果您启用该hide_user_chrome
选项,并且由于某种原因需要将其恢复,比如安装扩展程序,只需删除生成的~/.mozilla/firefox/PROFILE/chrome/userChrome.css
文件并重新启动配置文件。
答案2
我认为这也适合这里:我想出了这个令人憎恶的东西(个人意见...我只是想让它与 sh 一起工作:))它有一些技巧,但对于两个 Windows 来说非常强大..您也可以指定更多窗口,但必须通过添加更多窗口"specificprofile1"
以及相应的功能来手动添加它们..也许有人可以让它有更多..逻辑!
#!/bin/bash
# chromium-start.sh $1
# e.g. put:
# chrome-start.sh "Profile 1" to .desktop Exec=
# wmctrl -o 1366,0 ; chromium-browser %U --profile-directory=Profile\ 2 & sleep 3; wmctrl -o 0,0
# $1 = Profile folder name
profilename=$1
#2nd Chromium profile
specificprofile1="Profile 1"
echo "starting Chromium"
echo "args: " $1
echo "Profile name: " $profilename
echo "Specific profile: " $specificprofile1
# Just setting Chromium scaling variable, because of course Google Devs don't care about no fractional scaling on linux
scale_var=0.8
# Check if Chromium window with the specified class already exists
# Also allows using icons as "taskbar" switches (clicking icon takes to corresponding Chromium Window)
if wmctrl -l -x | grep "chromium-$profilename"
then
echo "Chromium Window exists, moving focus to it"
wmctrl -x -R chromium-"$profilename"
echo "true"
# Check if 2nd profile $specifiedprofile1 has been started yet or not. The WMCLASS(es) has to have been set correctly...
elif [[ "$specificprofile1" == "$profilename" ]] && [[ ! "`wmctrl -l -x | grep chromium-"$specificprofile1"`" ]]
then
# TODO: Nesting
if [ "$specificprofile1" == "$profilename" ]
then
echo $specificprofile1 "equals" $profilename
fi
echo "#2 Chromium Window for $specificprofile1 does not exist"
# wmctrl moves to specific position of desktop (1366 means moving to the following workspace since my resolution is 1366x768)
# Be careful if using sleep timing, since the command needs to have enough time to execute to have the window in the correct workspace
wmctrl -o 1366,0
chromium-browser --profile-directory="$profilename" --force-device-scale-factor=$scale_var %U &
# https://askubuntu.com/a/626524/654028
# Set's the chromium window which was opened latest to have a custom class, since Chromium doesn't care about the --class= flag...
# It has it's limitations, but should be robust enough for most use... Has not been tested long term.. Something probably could reset the WM_CLASS again
# xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$specificprofile1"
# Alternative method for checking if a window with specified class exists
# xprop -id "$(wmctrl -l -x| grep "chromium-$profilename" | tail -n 1 |awk '{ print $1 }')" | grep -o "WM_CLASS(STRING) = ".*"" | grep -o '".*"' | tr -d '"'
# https://stackoverflow.com/a/19441380/5776626
winrep=""
while [[ ! "`echo $winrep | grep -l "Map State: IsViewable"`" ]]
do
winid="$(wmctrl -l -x| grep "chromium-$profilename" | tail -n 1 |awk '{ print $1 }')"
# print $winid
winrep="$(xwininfo -id $winid | grep -o 'Map State: IsViewable')"
# print $winrep
sleep 0.75
xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$specificprofile1"
done
# sleep 3
# Move Window directly to workspace (#2 with 1366x768 resolution x = 1366), optionally comment out wmctrl -o 1366,0
# wmctrl -v -i -r $winid -e 0,1366,0,-1,-1
# sleep 5
# Move back to workspace #1
wmctrl -o 0,0
elif ! wmctrl -l -x | grep chromium-"$profilename"
then
echo "#3 Chromium Window $profilename does not exist"
wmctrl -o 0,0
chromium-browser --profile-directory="$profilename" --force-device-scale-factor=$scale_var %U &
# https://askubuntu.com/a/626524/654028
# ....
# sleep 3
winrep=""
while [[ ! "`echo $winrep | grep -l "Map State: IsViewable"`" ]]
do
winid="$(wmctrl -l -x| grep "chromium-$profilename" | tail -n 1 |awk '{ print $1 }')"
# print $winid
winrep="$(xwininfo -id $winid | grep -o 'Map State: IsViewable')"
# print $winrep
sleep 0.75
xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$profilename"
done
wmctrl -o 0,0
# xprop -id "$(wmctrl -l -x| grep "chromium-browser" | tail -n 1 |awk '{ print $1 }')" -f WM_CLASS 8s -set WM_CLASS "chromium-browser.chromium-$profilename"
fi
问题:
打印出现错误(弃用警告..):
Unescaped left brace in regex is deprecated, passed through in regex; marked by <-- HERE in m/%{ <-- HERE (.*?)}/ at /usr/bin/print line 528.
为了进行调试,您可以在使用实际图标时使用以下内容打印出错误: https://askubuntu.com/a/664272/654028(# 手动替代方案)
awk '/^Exec=/ {sub("^Exec=", ""); gsub(" ?%[cDdFfikmNnUuv]", ""); exit system($0)}' chrome-ws2.desktop
while 循环出错,可能是因为循环间隔
Error: no such file "at while function"
xwininfo: error: -id requires argument
xprop: error: Invalid window id format: .
xwininfo: error: -id requires argument
xprop: error: Invalid window id format: .
xwininfo: error: -id requires argument
xprop: error: Invalid window id format: .
另外,当过快单击相应的 .desktop 图标时(在设置自定义类之前?),将会打开一个新窗口。
(相当)有时当从 Chromium 启动得太快(~<3 秒)时,先前打开的窗口的类会被重置为 chromium-browser.chromium-browser。然后您可以预期图标会被交换或出现其他意外行为。