Harden DWM plugins against X11 stacking/keyboard grab races and speed up QMI status

This commit is contained in:
Luxferre
2026-10-25 14:14:21 +02:00
parent b2216c186f
commit e77be498bf
44 changed files with 432 additions and 211 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -65,10 +65,7 @@ detect_qmicli_modem() {
if ! command -v qmicli >/dev/null 2>&1; then
return 1
fi
if timeout 3 qmicli --silent -pd qrtr://0 --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="timeout 4 qmicli --silent -pd qrtr://0"
return 0
elif [ -e "/dev/wwan0qmi0" ]; then
if [ -e "/dev/wwan0qmi0" ]; then
if timeout 3 qmicli --silent -pd /dev/wwan0qmi0 --device-open-qmi --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="timeout 4 qmicli --silent -pd /dev/wwan0qmi0 --device-open-qmi"
return 0
@@ -76,6 +73,10 @@ detect_qmicli_modem() {
QMICLI_MODEM="timeout 4 qmicli --silent -d /dev/wwan0qmi0 --device-open-qmi"
return 0
fi
fi
if timeout 3 qmicli --silent -pd qrtr://0 --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="timeout 4 qmicli --silent -pd qrtr://0"
return 0
elif [ -e "/dev/modem" ]; then
if timeout 3 qmicli --silent -pd /dev/modem --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="timeout 4 qmicli --silent -pd /dev/modem"
@@ -636,26 +637,23 @@ case "$cmd" in
;;
status)
if ! detect_qmicli_modem; then
echo "MODEM_AVAILABLE=0"
echo "PREFERRED_SLOT=$PREFERRED_SIM_SLOT"
exit 0
QMI_CARDS=""
if command -v qmicli >/dev/null 2>&1; then
if [ -e "/dev/wwan0qmi0" ]; then
QMI_CARDS="$(timeout 3 qmicli --silent -pd /dev/wwan0qmi0 --device-open-qmi --uim-get-card-status 2>/dev/null || true)"
if [ -n "$QMI_CARDS" ]; then
QMICLI_MODEM="timeout 3 qmicli --silent -pd /dev/wwan0qmi0 --device-open-qmi"
fi
fi
if [ -z "$QMI_CARDS" ] && detect_qmicli_modem; then
QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
fi
fi
QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
if [ -z "$QMI_CARDS" ]; then
echo "MODEM_AVAILABLE=0"
echo "PREFERRED_SLOT=$PREFERRED_SIM_SLOT"
exit 0
fi
eval "$(parse_uim_card_status "$QMI_CARDS" "$PREFERRED_SIM_SLOT")"
if [ "$PRIMARY_GW_SLOT" = "0" ] && { [ "$SLOT1_STATE" = "present" ] || [ "$SLOT2_STATE" = "present" ]; }; then
SIM_WAIT_TIME=1
provision_slot "$PREFERRED_SIM_SLOT" 2 >/dev/null 2>&1 || true
QMI_CARDS_NEW="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
if [ -n "$QMI_CARDS_NEW" ]; then
QMI_CARDS="$QMI_CARDS_NEW"
fi
fi
echo "MODEM_AVAILABLE=1"
parse_uim_card_status "$QMI_CARDS" "$PREFERRED_SIM_SLOT"
QMI_NAS="$($QMICLI_MODEM --nas-get-serving-system 2>/dev/null || true)"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+13 -8
View File
@@ -333,6 +333,7 @@ int calllog_run_main(int argc, char** argv) {
XSync(dpy, False);
auto showWindow = [&]() {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("calllog");
bool themeChanged = theme.checkAndReload(
dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
@@ -348,13 +349,7 @@ int calllog_run_main(int argc, char** argv) {
if (!themeChanged) {
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
}
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(2000);
}
grabPluginKeyboard(dpy, win);
if (reloadLogsIfNeeded() || themeChanged || filterTab != 0 || selected != 0) {
filterTab = 0;
selected = 0;
@@ -362,8 +357,9 @@ int calllog_run_main(int argc, char** argv) {
render();
}
XFlush(dpy);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
if (reloadLogsIfNeeded() || themeChanged) {
render();
}
@@ -444,6 +440,15 @@ int calllog_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("calllog")) {
hideWindow(false);
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
+12 -8
View File
@@ -926,6 +926,7 @@ int callscreen_run_main(int argc, char** argv) {
XSync(dpy, False);
auto showWindow = [&]() {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("callscreen");
wakeDisplayHardware(dpy);
theme.checkAndReload(dpy, screen, visual, cmap, layoutMain, layoutSmall,
@@ -942,17 +943,11 @@ int callscreen_run_main(int argc, char** argv) {
visible = true;
XMapRaised(dpy, win);
render();
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(2000);
}
grabPluginKeyboard(dpy, win);
XFlush(dpy);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
} else {
XMapRaised(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
};
@@ -1379,6 +1374,15 @@ int callscreen_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("callscreen")) {
hideWindow(false);
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||
+14 -10
View File
@@ -711,26 +711,19 @@ int runCameraApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& argStr) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("camera");
countdownActive = false;
torchOn = getTorchState();
torchRestoredOnClose = !torchOn;
writeIntFile(activePidPath, static_cast<int>(::getpid()));
if (!visible) {
visible = true;
XMapRaised(dpy, win);
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(2000);
}
} else {
XRaiseWindow(dpy, win);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
grabPluginKeyboard(dpy, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -822,6 +815,15 @@ int runCameraApp(int argc, char** argv) {
if (ev.type == Expose && ev.xexpose.count == 0) {
drawUI();
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("camera")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
drawUI();
}
} else if (ev.type == KeyPress) {
int64_t tNow = nowMs();
unsigned int kc = ev.xkey.keycode;
@@ -925,7 +927,9 @@ int runCameraApp(int argc, char** argv) {
enterViewfinderMode();
drawUI();
} else {
runAsyncArgv("feh", photos[selectedPhoto].fullPath.c_str());
std::string path = photos[selectedPhoto].fullPath;
hideWindow();
runAsyncArgv("feh", path.c_str());
}
}
} else if (mode == ViewMode::ConfirmDelete) {
+28 -11
View File
@@ -765,6 +765,7 @@ int runContactsApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& arg) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("contacts");
bool themeChanged = theme.checkAndReload(
dpy, screen, visual, cmap, layoutMain, layoutSmall, layoutIcon,
@@ -784,13 +785,7 @@ int runContactsApp(int argc, char** argv) {
if (!openNewMode && !themeChanged) {
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
}
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(2000);
}
grabPluginKeyboard(dpy, win);
bool reloaded = reloadContactsIfNeeded();
if (openNewMode) {
beginNewContact(prefill);
@@ -804,8 +799,9 @@ int runContactsApp(int argc, char** argv) {
render();
}
XFlush(dpy);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
reloadContactsIfNeeded();
if (openNewMode) {
beginNewContact(prefill);
@@ -914,9 +910,13 @@ int runContactsApp(int argc, char** argv) {
render();
int rc = 0;
std::string out = runCaptureCmd(
"doas /usr/libexec/msm-modem-uim-selection sim-contacts-import " +
"doas -n /usr/libexec/msm-modem-uim-selection sim-contacts-import " +
std::to_string(slotNum) + " 2>&1",
&rc);
if (visible) {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
}
if (out.rfind("ERROR:", 0) == 0) {
std::string err = trimStr(out.substr(6));
setStatus(err.empty() ? "SIM import failed" : err, true);
@@ -975,9 +975,13 @@ int runContactsApp(int argc, char** argv) {
render();
int rc = 0;
std::string out = runCaptureCmd(
"doas /usr/libexec/msm-modem-uim-selection sim-contacts-export " +
"doas -n /usr/libexec/msm-modem-uim-selection sim-contacts-export " +
std::to_string(slotNum) + " " + shellQuote(srcFile) + " 2>&1",
&rc);
if (visible) {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
}
if (!tmpSingle.empty()) {
::unlink(tmpSingle.c_str());
}
@@ -1015,7 +1019,11 @@ int runContactsApp(int argc, char** argv) {
prevModeBeforeSim =
(op == SimOp::ExportSingle) ? ViewMode::Detail : ViewMode::List;
std::string st = runCaptureCmd(
"doas /usr/libexec/msm-modem-uim-selection status 2>/dev/null");
"doas -n /usr/libexec/msm-modem-uim-selection status 2>/dev/null");
if (visible) {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
}
bool s1Present = false;
bool s2Present = false;
int gwSlot = 1;
@@ -1203,6 +1211,15 @@ int runContactsApp(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("contacts")) {
hideWindow(false);
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (ev.xkey.keycode == 19 || sym == XK_0) {
+17 -8
View File
@@ -509,10 +509,15 @@ int dialer_run_main(int argc, char** argv) {
ussdReplyInput.clear();
render();
ussdResponse = sendModemUssd(codeOrReply, isReply, &ussdSessionActive);
if (visible) {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
}
render();
};
auto showWindow = [&](const std::string& arg) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("dialer");
modal = DialerModal::Inactive;
bool themeChanged = theme.checkAndReload(
@@ -532,13 +537,7 @@ int dialer_run_main(int argc, char** argv) {
if (cleanArg.empty() && !themeChanged) {
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
}
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(2000);
}
grabPluginKeyboard(dpy, win);
bool reloaded = reloadContactsIfNeeded();
if (cleanArg == "*#06#") {
openImeiModal();
@@ -549,8 +548,9 @@ int dialer_run_main(int argc, char** argv) {
render();
}
XFlush(dpy);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
} else if (!cleanArg.empty()) {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
if (nowMs() - openStartMs < 40 && number == cleanArg) {
return;
}
@@ -675,6 +675,15 @@ int dialer_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("dialer")) {
hideWindow(false);
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (ev.xkey.keycode == 19 || sym == XK_0) {
+23 -21
View File
@@ -530,6 +530,16 @@ int runFilesApp(int argc, char** argv) {
actionSelected = 0;
};
auto hideWindow = [&]() {
if (!visible) return;
writeSoftkeySuppress(nowMs() + 450);
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XFlush(dpy);
visible = false;
::unlink(activePidPath.c_str());
};
auto openSelectedItem = [&]() {
if (filteredItems.empty()) return;
const FileItem item = filteredItems[selected];
@@ -549,9 +559,9 @@ int runFilesApp(int argc, char** argv) {
if (isImageExt(ext)) {
openImageViewer(item);
} else if (isMediaExt(ext)) {
hideWindow();
runAsyncArgv("sxmo_open.sh", item.fullPath.c_str());
statusBanner = "Opening in media player...";
statusBannerUntilMs = nowMs() + 1800;
return;
} else if (isLikelyTextFile(item.fullPath, item.size)) {
openTextViewer(item);
} else {
@@ -889,24 +899,17 @@ int runFilesApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& dirArg) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("files");
mode = ViewMode::Browser;
loadDirectory(dirArg.empty() ? currentDir : dirArg);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
if (!visible) {
visible = true;
XMapRaised(dpy, win);
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(2000);
}
} else {
XRaiseWindow(dpy, win);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
grabPluginKeyboard(dpy, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -914,16 +917,6 @@ int runFilesApp(int argc, char** argv) {
drawUI();
};
auto hideWindow = [&]() {
if (!visible) return;
writeSoftkeySuppress(nowMs() + 450);
XUngrabKeyboard(dpy, CurrentTime);
XUnmapWindow(dpy, win);
XFlush(dpy);
visible = false;
::unlink(activePidPath.c_str());
};
auto commitModalTap = [&](int digit) {
const char* chars = kMultiTapFile[digit];
int len = static_cast<int>(std::strlen(chars));
@@ -1157,6 +1150,15 @@ int runFilesApp(int argc, char** argv) {
if (ev.type == Expose && ev.xexpose.count == 0) {
drawUI();
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("files")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
drawUI();
}
} else if (ev.type == KeyPress) {
int64_t tNow = nowMs();
unsigned int kc = ev.xkey.keycode;
+48 -20
View File
@@ -364,13 +364,26 @@ ModemStatusData queryModemAndSimStatus() {
}
// 3. Query active SIM object if available
static std::string cachedSimPath;
static std::string cachedSimIccid;
static std::string cachedSimOperator;
if (!d.simDbusPath.empty()) {
std::string simOut = runCaptureCmd(
"mmcli -i " + shellQuote(d.simDbusPath) + " -K 2>/dev/null");
std::unordered_map<std::string, std::string> simKv;
parseKvLines(simOut, simKv, ':');
d.simIccid = cleanMmVal(simKv["sim.properties.iccid"]);
d.simOperatorName = cleanMmVal(simKv["sim.properties.operator-name"]);
if (d.simDbusPath == cachedSimPath && !cachedSimIccid.empty()) {
d.simIccid = cachedSimIccid;
d.simOperatorName = cachedSimOperator;
} else {
std::string simOut = runCaptureCmd(
"mmcli -i " + shellQuote(d.simDbusPath) + " -K 2>/dev/null");
std::unordered_map<std::string, std::string> simKv;
parseKvLines(simOut, simKv, ':');
d.simIccid = cleanMmVal(simKv["sim.properties.iccid"]);
d.simOperatorName = cleanMmVal(simKv["sim.properties.operator-name"]);
if (!d.simIccid.empty()) {
cachedSimPath = d.simDbusPath;
cachedSimIccid = d.simIccid;
cachedSimOperator = d.simOperatorName;
}
}
if (d.operatorName.empty() && !d.simOperatorName.empty()) {
d.operatorName = d.simOperatorName;
}
@@ -609,6 +622,10 @@ int runModemApp(int argc, char** argv) {
}
}
lastRefreshMs = nowMs();
if (visible && !isAnotherPluginActive("modem")) {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
}
};
auto defaultActiveOrPresentSlot = [&]() -> int {
@@ -1222,13 +1239,7 @@ int runModemApp(int argc, char** argv) {
};
auto grabKeys = [&]() {
for (int i = 0; i < 6; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(15000);
}
grabPluginKeyboard(dpy, win);
};
auto showWindow = [&](const std::string& arg = "") {
@@ -1236,21 +1247,28 @@ int runModemApp(int argc, char** argv) {
if (visible &&
(modal == ModalMode::VerifyPin || modal == ModalMode::UnblockPuk) &&
(a == "unlock" || a == "pin" || a == "puk")) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("modem");
XRaiseWindow(dpy, win);
grabKeys();
render();
return;
}
closeOtherPlugins("modem");
refreshData(true);
applyOpenArg(arg);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("modem");
if (!visible) {
XMapRaised(dpy, win);
visible = true;
} else {
XRaiseWindow(dpy, win);
}
grabKeys();
render();
refreshData(true);
applyOpenArg(arg);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("modem");
XRaiseWindow(dpy, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -1446,6 +1464,10 @@ int runModemApp(int argc, char** argv) {
if (ussdWaitingReply) {
ussdInput.clear();
}
if (visible) {
XRaiseWindow(dpy, win);
grabKeys();
}
}
};
@@ -1693,7 +1715,13 @@ int runModemApp(int argc, char** argv) {
render();
} else if (ev.type == VisibilityNotify && visible &&
ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
if (isAnotherPluginActive("modem")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabKeys();
render();
}
} else if (ev.type == KeyRelease) {
if (!initialSoftkeysReleased && querySoftkeysDown(dpy) == 0) {
initialSoftkeysReleased = true;
@@ -2033,14 +2061,14 @@ int runModemApp(int argc, char** argv) {
timeoutMs = static_cast<int>(bRem);
}
}
// Periodic 5-second status refresh while visible on SIM/Radio tabs
// Periodic 10-second status refresh while visible on SIM/Radio tabs
if (modal == ModalMode::None && activeTab != TabId::ModemLog &&
activeTab != TabId::AtConsole) {
int64_t rRem = (lastRefreshMs + 5000) - now;
int64_t rRem = (lastRefreshMs + 10000) - now;
if (rRem <= 0) {
refreshData(false);
render();
rRem = 5000;
rRem = 10000;
}
if (timeoutMs < 0 || rRem < timeoutMs) {
timeoutMs = static_cast<int>(rRem);
+15 -10
View File
@@ -923,25 +923,24 @@ int runNetworksApp(int argc, char** argv) {
};
auto grabKeys = [&]() {
for (int i = 0; i < 6; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(15000);
}
grabPluginKeyboard(dpy, win);
};
auto showWindow = [&](const std::string& arg = "") {
closeOtherPlugins("networks");
applyOpenArg(arg);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("networks");
if (!visible) {
XMapRaised(dpy, win);
visible = true;
} else {
XRaiseWindow(dpy, win);
}
grabKeys();
render();
applyOpenArg(arg);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("networks");
XRaiseWindow(dpy, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -1256,7 +1255,13 @@ int runNetworksApp(int argc, char** argv) {
render();
} else if (ev.type == VisibilityNotify && visible &&
ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
if (isAnotherPluginActive("networks")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabKeys();
render();
}
} else if (ev.type == KeyRelease) {
if (!initialSoftkeysReleased && querySoftkeysDown(dpy) == 0) {
initialSoftkeysReleased = true;
+103 -43
View File
@@ -26,6 +26,7 @@
#include <string>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <unordered_map>
#include <unordered_set>
@@ -339,18 +340,24 @@ inline bool isKeycodeDown(Display* d, int keycode) {
inline void runAsyncCmd(const std::string& cmd) {
pid_t pid = ::fork();
if (pid == 0) {
::setsid();
int nullfd = ::open("/dev/null", O_RDWR);
if (nullfd >= 0) {
::dup2(nullfd, STDIN_FILENO);
::dup2(nullfd, STDOUT_FILENO);
::dup2(nullfd, STDERR_FILENO);
if (nullfd > 2) {
::close(nullfd);
if (::fork() == 0) {
::setsid();
int nullfd = ::open("/dev/null", O_RDWR);
if (nullfd >= 0) {
::dup2(nullfd, STDIN_FILENO);
::dup2(nullfd, STDOUT_FILENO);
::dup2(nullfd, STDERR_FILENO);
if (nullfd > 2) {
::close(nullfd);
}
}
execl("/bin/sh", "sh", "-c", cmd.c_str(), static_cast<char*>(nullptr));
_exit(0);
}
execl("/bin/sh", "sh", "-c", cmd.c_str(), static_cast<char*>(nullptr));
_exit(0);
} else if (pid > 0) {
int st = 0;
(void)::waitpid(pid, &st, 0);
}
}
@@ -393,42 +400,48 @@ inline void runAsyncArgv(const char* prog,
const char* arg3 = nullptr) {
pid_t pid = ::fork();
if (pid == 0) {
::setsid();
int nullfd = ::open("/dev/null", O_RDWR);
if (nullfd >= 0) {
::dup2(nullfd, STDIN_FILENO);
::dup2(nullfd, STDOUT_FILENO);
::dup2(nullfd, STDERR_FILENO);
if (nullfd > 2) {
::close(nullfd);
if (::fork() == 0) {
::setsid();
int nullfd = ::open("/dev/null", O_RDWR);
if (nullfd >= 0) {
::dup2(nullfd, STDIN_FILENO);
::dup2(nullfd, STDOUT_FILENO);
::dup2(nullfd, STDERR_FILENO);
if (nullfd > 2) {
::close(nullfd);
}
}
}
if (arg1 && arg2 && arg3) {
execlp(prog, prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
} else if (arg1 && arg2) {
execlp(prog, prog, arg1, arg2, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
} else if (arg1) {
execlp(prog, prog, arg1, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
} else {
execlp(prog, prog, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, static_cast<char*>(nullptr));
if (arg1 && arg2 && arg3) {
execlp(prog, prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, arg2, arg3, static_cast<char*>(nullptr));
} else if (arg1 && arg2) {
execlp(prog, prog, arg1, arg2, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, arg2, static_cast<char*>(nullptr));
} else if (arg1) {
execlp(prog, prog, arg1, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, arg1, static_cast<char*>(nullptr));
} else {
execlp(prog, prog, static_cast<char*>(nullptr));
std::string localBin = std::string("/home/user/.local/bin/") + prog;
execl(localBin.c_str(), prog, static_cast<char*>(nullptr));
std::string usrBin = std::string("/usr/bin/") + prog;
execl(usrBin.c_str(), prog, static_cast<char*>(nullptr));
}
_exit(0);
}
_exit(0);
} else if (pid > 0) {
int st = 0;
(void)::waitpid(pid, &st, 0);
}
}
@@ -1152,11 +1165,58 @@ inline void closeOtherPlugins(const char* selfPluginName) {
sendPluginCommand(p.name, "CLOSE");
}
}
if (::access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0) {
if ((!selfPluginName || std::strcmp(selfPluginName, "dmenu") != 0) &&
::access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0) {
sendPluginCommand("dmenu", "CLOSE");
}
}
inline bool isAnotherPluginActive(const char* selfPluginName) {
struct PluginRef {
const char* pidFile;
const char* name;
};
static const PluginRef kAllPlugins[] = {
{"xdrawer.pid", "xdrawer"},
{"xdialer.pid", "dialer"},
{"xcalllog.pid", "calllog"},
{"xcallscreen.pid", "callscreen"},
{"xcontacts.pid", "contacts"},
{"xsms.pid", "sms"},
{"xfiles.pid", "files"},
{"xcamera.pid", "camera"},
{"xthemes.pid", "themes"},
{"xmodem.pid", "modem"},
{"xnetworks.pid", "networks"},
{"xringtones.pid", "ringtones"},
{"xwallpapers.pid", "wallpapers"},
};
for (const auto& p : kAllPlugins) {
if (selfPluginName && std::strcmp(p.name, selfPluginName) == 0) {
continue;
}
if (isPidFileAlive(getRuntimeFile(p.pidFile))) {
return true;
}
}
if ((!selfPluginName || std::strcmp(selfPluginName, "dmenu") != 0) &&
::access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0) {
return true;
}
return false;
}
inline void grabPluginKeyboard(Display* dpy, Window win) {
for (int i = 0; i < 40; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
::usleep(3000);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
}
inline void broadcastThemeReload(const char* selfPluginName = nullptr) {
static const char* kPlugins[] = {
"dmenu",
+9 -10
View File
@@ -576,6 +576,7 @@ int runRingtonesApp(int argc, char** argv) {
};
auto showWindow = [&]() {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("ringtones");
refreshData();
for (size_t i = 0; i < tones.size(); ++i) {
@@ -584,7 +585,6 @@ int runRingtonesApp(int argc, char** argv) {
break;
}
}
writeIntFile(activePidPath, static_cast<int>(::getpid()));
if (!visible) {
visible = true;
XMapRaised(dpy, win);
@@ -592,14 +592,7 @@ int runRingtonesApp(int argc, char** argv) {
} else {
XRaiseWindow(dpy, win);
}
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(2000);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
grabPluginKeyboard(dpy, win);
startupIgnoreUntilMs = nowMs() + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
softkeyPending = 0;
@@ -688,7 +681,13 @@ int runRingtonesApp(int argc, char** argv) {
render();
} else if (ev.type == VisibilityNotify && visible &&
ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
if (isAnotherPluginActive("ringtones")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
} else if (ev.type == KeyRelease) {
if (!initialSoftkeysReleased && querySoftkeysDown(dpy) == 0) {
initialSoftkeysReleased = true;
+11 -9
View File
@@ -862,6 +862,7 @@ int runSmsApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& argStr) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("sms");
reloadAllData(true);
lastTapDigit = -1;
@@ -881,21 +882,13 @@ int runSmsApp(int argc, char** argv) {
selectedThread = 0;
}
writeIntFile(activePidPath, static_cast<int>(::getpid()));
if (!visible) {
visible = true;
XMapRaised(dpy, win);
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(2000);
}
} else {
XRaiseWindow(dpy, win);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
grabPluginKeyboard(dpy, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -1117,6 +1110,15 @@ int runSmsApp(int argc, char** argv) {
if (ev.type == Expose && ev.xexpose.count == 0) {
drawUI();
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("sms")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
drawUI();
}
} else if (ev.type == KeyRelease) {
unsigned int kc = ev.xkey.keycode;
KeySym sym = XLookupKeysym(&ev.xkey, 0);
+11 -9
View File
@@ -871,24 +871,17 @@ int runThemesApp(int argc, char** argv) {
};
auto showWindow = [&](const std::string& argStr) {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("themes");
mode = ViewMode::Browser;
refreshThemes(argStr);
writeIntFile(activePidPath, static_cast<int>(::getpid()));
if (!visible) {
visible = true;
XMapRaised(dpy, win);
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(2000);
}
} else {
XRaiseWindow(dpy, win);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
grabPluginKeyboard(dpy, win);
openStartMs = nowMs();
startupIgnoreUntilMs = openStartMs + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
@@ -1132,6 +1125,15 @@ int runThemesApp(int argc, char** argv) {
if (ev.type == Expose && ev.xexpose.count == 0) {
drawUI();
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isAnotherPluginActive("themes")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
drawUI();
}
} else if (ev.type == KeyPress) {
int64_t tNow = nowMs();
unsigned int kc = ev.xkey.keycode;
+9 -10
View File
@@ -504,6 +504,7 @@ int runWallpapersApp(int argc, char** argv) {
};
auto showWindow = [&]() {
writeIntFile(activePidPath, static_cast<int>(::getpid()));
closeOtherPlugins("wallpapers");
fullscreenPreview = false;
refreshData();
@@ -514,7 +515,6 @@ int runWallpapersApp(int argc, char** argv) {
}
}
updatePreviewCache();
writeIntFile(activePidPath, static_cast<int>(::getpid()));
if (!visible) {
visible = true;
XMapRaised(dpy, win);
@@ -522,14 +522,7 @@ int runWallpapersApp(int argc, char** argv) {
} else {
XRaiseWindow(dpy, win);
}
for (int i = 0; i < 20; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync,
CurrentTime) == GrabSuccess) {
break;
}
usleep(2000);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
grabPluginKeyboard(dpy, win);
startupIgnoreUntilMs = nowMs() + 180;
initialSoftkeysReleased = (querySoftkeysDown(dpy) == 0);
softkeyPending = 0;
@@ -583,7 +576,13 @@ int runWallpapersApp(int argc, char** argv) {
render();
} else if (ev.type == VisibilityNotify && visible &&
ev.xvisibility.state != VisibilityUnobscured) {
XRaiseWindow(dpy, win);
if (isAnotherPluginActive("wallpapers")) {
hideWindow();
} else {
XRaiseWindow(dpy, win);
grabPluginKeyboard(dpy, win);
render();
}
} else if (ev.type == KeyRelease) {
if (!initialSoftkeysReleased && querySoftkeysDown(dpy) == 0) {
initialSoftkeysReleased = true;
+63 -6
View File
@@ -475,19 +475,21 @@ grabfocus(void)
static void
grabkeyboard(void)
{
struct timespec ts = { .tv_sec = 0, .tv_nsec = 2000000 };
struct timespec ts = { .tv_sec = 0, .tv_nsec = 3000000 };
Window grabwin;
int i;
if (embed || managed)
return;
grabwin = daemon_mode ? win : DefaultRootWindow(dpy);
for (i = 0; i < 25; i++) {
for (i = 0; i < 40; i++) {
if (XGrabKeyboard(dpy, grabwin, True, GrabModeAsync,
GrabModeAsync, CurrentTime) == GrabSuccess)
return;
break;
nanosleep(&ts, NULL);
}
if (win)
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
}
static void
@@ -2385,6 +2387,46 @@ open_builtin_menu(const char *menu_name, int keep_idx)
show_menu();
}
static int
any_other_plugin_active(void)
{
static const char *pid_files[] = {
"xdrawer.pid",
"xdialer.pid",
"xcalllog.pid",
"xcallscreen.pid",
"xcontacts.pid",
"xsms.pid",
"xfiles.pid",
"xcamera.pid",
"xthemes.pid",
"xmodem.pid",
"xnetworks.pid",
"xringtones.pid",
"xwallpapers.pid",
};
const char *rdir = getenv("XDG_RUNTIME_DIR");
const char *base = (rdir && rdir[0]) ? rdir : "/run/user/10000";
for (size_t i = 0; i < LENGTH(pid_files); i++) {
char ppath[256];
snprintf(ppath, sizeof(ppath), "%s/%s", base, pid_files[i]);
if (access(ppath, F_OK) != 0)
snprintf(ppath, sizeof(ppath), "/tmp/%s", pid_files[i]);
if (access(ppath, F_OK) == 0) {
FILE *pf = fopen(ppath, "r");
if (pf) {
int pid = -1;
if (fscanf(pf, "%d", &pid) == 1 && pid > 0 && kill(pid, 0) == 0) {
fclose(pf);
return 1;
}
fclose(pf);
}
}
}
return 0;
}
static void
close_other_active_plugins(void)
{
@@ -2639,6 +2681,8 @@ show_menu(void)
soft_deadline = 0;
if (!menu_active) {
menu_active = 1;
mark_menu_active(1);
close_other_active_plugins();
prevfocuswin = None;
Window fw = None;
@@ -2654,8 +2698,6 @@ show_menu(void)
read_softkey_state(&init_soft_mask, &init_soft_ms);
start_ignore = dmenu_now_ms() + 180;
initial_released = (query_trigger_keys_down() == 0);
menu_active = 1;
mark_menu_active(1);
drawmenu();
XMapRaised(dpy, win);
drawmenu();
@@ -2666,11 +2708,13 @@ show_menu(void)
XSetICFocus(xic);
XFlush(dpy);
} else {
close_other_active_plugins();
init_soft_mask = 0;
init_soft_ms = 0;
start_ignore = dmenu_now_ms() + 60;
initial_released = (query_trigger_keys_down() == 0);
XRaiseWindow(dpy, win);
grabkeyboard();
drawmenu();
if (xic && engage_ime)
XSetICFocus(xic);
@@ -2975,6 +3019,19 @@ process_x11_events(void)
if (ev.xexpose.count == 0 && (menu_active || !daemon_mode))
drw_map(drw, win, 0, 0, mw, mh);
break;
case VisibilityNotify:
if (ev.xvisibility.state != VisibilityUnobscured &&
(menu_active || !daemon_mode)) {
if (any_other_plugin_active()) {
if (daemon_mode)
cancel_session();
} else {
XRaiseWindow(dpy, win);
grabkeyboard();
drw_map(drw, win, 0, 0, mw, mh);
}
}
break;
case FocusIn:
if (menu_active || !daemon_mode) {
if (!daemon_mode && ev.xfocus.window != win)
@@ -3186,7 +3243,7 @@ setup(int map_immediately)
swa.override_redirect = managed ? False : True;
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | VisibilityChangeMask;
win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+40 -10
View File
@@ -771,12 +771,32 @@ static int xdrawer_run_main(int argc, char** argv) {
}
};
auto isOtherPluginActive = [&]() {
static const char* kPids[] = {
"xdialer.pid", "xcalllog.pid", "xcallscreen.pid", "xcontacts.pid",
"xsms.pid", "xfiles.pid", "xcamera.pid", "xthemes.pid",
"xmodem.pid", "xnetworks.pid", "xringtones.pid", "xwallpapers.pid",
};
for (const char* pf : kPids) {
int p = readIntFile(getRuntimeFile(pf), -1);
if (p > 0 && ::kill(p, 0) == 0) {
return true;
}
}
if (::access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0) {
return true;
}
return false;
};
auto showWindow = [&]() {
if (visible) {
return;
}
writeIntFile(pidPath, static_cast<int>(::getpid()));
closeOtherPlugin("xdialer.pid", "/tmp/dwm_plugin_dialer.fifo");
closeOtherPlugin("xcalllog.pid", "/tmp/dwm_plugin_calllog.fifo");
closeOtherPlugin("xcallscreen.pid", "/tmp/dwm_plugin_callscreen.fifo");
closeOtherPlugin("xcontacts.pid", "/tmp/dwm_plugin_contacts.fifo");
closeOtherPlugin("xsms.pid", "/tmp/dwm_plugin_sms.fifo");
closeOtherPlugin("xfiles.pid", "/tmp/dwm_plugin_files.fifo");
@@ -786,6 +806,13 @@ static int xdrawer_run_main(int argc, char** argv) {
closeOtherPlugin("xnetworks.pid", "/tmp/dwm_plugin_networks.fifo");
closeOtherPlugin("xringtones.pid", "/tmp/dwm_plugin_ringtones.fifo");
closeOtherPlugin("xwallpapers.pid", "/tmp/dwm_plugin_wallpapers.fifo");
if (::access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0) {
int dfd = ::open("/tmp/dwm_plugin_dmenu.fifo", O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (dfd >= 0) {
(void)::write(dfd, "CLOSE\n", 6);
::close(dfd);
}
}
theme.checkAndReload(dpy, screen, visual, cmap, layoutMain, layoutSmall,
layoutIcon, nullptr, win);
openStartMs = nowMs();
@@ -794,24 +821,17 @@ static int xdrawer_run_main(int argc, char** argv) {
visible = true;
XMapRaised(dpy, win);
XCopyArea(dpy, pix, win, gc, 0, 0, kWinWidth, kWinHeight, 0, 0);
for (int i = 0; i < 20; ++i) {
for (int i = 0; i < 40; ++i) {
if (XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) ==
GrabSuccess) {
break;
}
usleep(2000);
usleep(3000);
}
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
XFlush(dpy);
writeIntFile(pidPath, static_cast<int>(::getpid()));
controller.refreshAll();
render();
if (::access("/tmp/dwm_plugin_dmenu.active", F_OK) == 0) {
int dfd = ::open("/tmp/dwm_plugin_dmenu.fifo", O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (dfd >= 0) {
(void)::write(dfd, "CLOSE\n", 6);
::close(dfd);
}
}
};
auto hideWindow = [&](bool drainSoftkeys) {
@@ -889,6 +909,16 @@ static int xdrawer_run_main(int argc, char** argv) {
if (ev.xexpose.count == 0) {
render();
}
} else if (ev.type == VisibilityNotify &&
ev.xvisibility.state != VisibilityUnobscured) {
if (isOtherPluginActive()) {
hideWindow(false);
} else {
XRaiseWindow(dpy, win);
XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime);
render();
}
} else if (ev.type == KeyRelease) {
KeySym sym = XLookupKeysym(&ev.xkey, 0);
if (sym == XK_Menu || sym == XF86XK_KbdLcdMenu1 ||