2022-03-28 16:10:30 +02:00
|
|
|
#include "MiscFunctions.hpp"
|
|
|
|
#include "../defines.hpp"
|
2022-05-05 12:50:25 +02:00
|
|
|
#include <algorithm>
|
2022-05-18 12:18:58 +02:00
|
|
|
#include "../Compositor.hpp"
|
2023-10-23 00:11:03 +02:00
|
|
|
#include <optional>
|
2023-05-08 15:37:59 +02:00
|
|
|
#include <set>
|
2022-06-25 20:49:06 +02:00
|
|
|
#include <sys/utsname.h>
|
2022-07-18 20:47:28 +02:00
|
|
|
#include <iomanip>
|
2023-01-31 01:03:23 +01:00
|
|
|
#include <sstream>
|
2023-10-11 23:27:53 +02:00
|
|
|
#ifdef HAS_EXECINFO
|
2023-08-11 17:22:16 +02:00
|
|
|
#include <execinfo.h>
|
2023-10-11 23:27:53 +02:00
|
|
|
#endif
|
2022-03-28 16:10:30 +02:00
|
|
|
|
2023-03-31 21:39:04 +02:00
|
|
|
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
2022-12-16 18:17:31 +01:00
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#if defined(__DragonFly__)
|
|
|
|
#include <sys/kinfo.h> // struct kinfo_proc
|
2023-03-31 21:39:04 +02:00
|
|
|
#elif defined(__FreeBSD__)
|
2022-12-16 18:17:31 +01:00
|
|
|
#include <sys/user.h> // struct kinfo_proc
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__NetBSD__)
|
|
|
|
#undef KERN_PROC
|
|
|
|
#define KERN_PROC KERN_PROC2
|
|
|
|
#define KINFO_PROC struct kinfo_proc2
|
|
|
|
#else
|
|
|
|
#define KINFO_PROC struct kinfo_proc
|
|
|
|
#endif
|
|
|
|
#if defined(__DragonFly__)
|
|
|
|
#define KP_PPID(kp) kp.kp_ppid
|
2023-03-31 21:39:04 +02:00
|
|
|
#elif defined(__FreeBSD__)
|
2022-12-16 18:17:31 +01:00
|
|
|
#define KP_PPID(kp) kp.ki_ppid
|
|
|
|
#else
|
|
|
|
#define KP_PPID(kp) kp.p_ppid
|
|
|
|
#endif
|
2022-11-07 21:26:23 +01:00
|
|
|
#endif
|
|
|
|
|
2023-01-24 22:44:54 +01:00
|
|
|
static const float transforms[][9] = {
|
|
|
|
{
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0.0f,
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
0.0f,
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
-1.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
0.0f,
|
|
|
|
1.0f,
|
2022-12-17 23:37:44 +01:00
|
|
|
},
|
2022-06-28 11:30:07 +02:00
|
|
|
};
|
|
|
|
|
2022-08-19 22:18:09 +02:00
|
|
|
std::string absolutePath(const std::string& rawpath, const std::string& currentPath) {
|
|
|
|
auto value = rawpath;
|
|
|
|
|
|
|
|
if (value[0] == '~') {
|
|
|
|
static const char* const ENVHOME = getenv("HOME");
|
|
|
|
value.replace(0, 1, std::string(ENVHOME));
|
2023-09-14 13:07:31 +02:00
|
|
|
} else if (value[0] != '/') {
|
|
|
|
auto currentDir = currentPath.substr(0, currentPath.find_last_of('/'));
|
|
|
|
|
|
|
|
if (value[0] == '.') {
|
|
|
|
if (value[1] == '.' && value[2] == '/') {
|
|
|
|
auto parentDir = currentDir.substr(0, currentDir.find_last_of('/'));
|
|
|
|
value.replace(0, 2 + currentPath.empty(), parentDir);
|
|
|
|
} else if (value[1] == '/')
|
|
|
|
value.replace(0, 1 + currentPath.empty(), currentDir);
|
|
|
|
else
|
|
|
|
value = currentDir + '/' + value;
|
|
|
|
} else
|
|
|
|
value = currentDir + '/' + value;
|
2022-08-19 22:18:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2022-12-16 18:17:31 +01:00
|
|
|
void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const std::string& ownerString) {
|
2022-03-28 16:10:30 +02:00
|
|
|
ASSERT(pSignal);
|
|
|
|
ASSERT(pListener);
|
2022-09-25 20:07:48 +02:00
|
|
|
|
2022-03-28 16:10:30 +02:00
|
|
|
wl_signal_add(pSignal, pListener);
|
|
|
|
|
2023-09-06 12:51:36 +02:00
|
|
|
Debug::log(LOG, "Registered signal for owner {:x}: {:x} -> {:x} (owner: {})", (uintptr_t)pOwner, (uintptr_t)pSignal, (uintptr_t)pListener, ownerString);
|
2022-04-02 19:09:27 +02:00
|
|
|
}
|
|
|
|
|
2022-12-16 18:17:31 +01:00
|
|
|
void handleNoop(struct wl_listener* listener, void* data) {
|
2022-04-02 19:09:27 +02:00
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
|
2022-07-18 20:47:28 +02:00
|
|
|
std::string escapeJSONStrings(const std::string& str) {
|
|
|
|
std::ostringstream oss;
|
2022-12-16 18:17:31 +01:00
|
|
|
for (auto& c : str) {
|
2022-07-18 20:47:28 +02:00
|
|
|
switch (c) {
|
2022-12-16 18:17:31 +01:00
|
|
|
case '"': oss << "\\\""; break;
|
|
|
|
case '\\': oss << "\\\\"; break;
|
|
|
|
case '\b': oss << "\\b"; break;
|
|
|
|
case '\f': oss << "\\f"; break;
|
|
|
|
case '\n': oss << "\\n"; break;
|
|
|
|
case '\r': oss << "\\r"; break;
|
|
|
|
case '\t': oss << "\\t"; break;
|
|
|
|
default:
|
|
|
|
if ('\x00' <= c && c <= '\x1f') {
|
|
|
|
oss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c);
|
|
|
|
} else {
|
|
|
|
oss << c;
|
|
|
|
}
|
2022-07-18 20:47:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
2022-04-18 13:25:27 +02:00
|
|
|
std::string removeBeginEndSpacesTabs(std::string str) {
|
2022-11-10 22:51:46 +01:00
|
|
|
if (str.empty())
|
|
|
|
return str;
|
|
|
|
|
2022-10-03 15:36:56 +02:00
|
|
|
int countBefore = 0;
|
|
|
|
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
|
|
|
|
countBefore++;
|
2022-04-18 13:25:27 +02:00
|
|
|
}
|
|
|
|
|
2022-10-03 15:36:56 +02:00
|
|
|
int countAfter = 0;
|
2023-03-05 19:16:42 +01:00
|
|
|
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
|
2022-10-03 15:36:56 +02:00
|
|
|
countAfter++;
|
2022-04-18 13:25:27 +02:00
|
|
|
}
|
|
|
|
|
2022-10-03 15:36:56 +02:00
|
|
|
str = str.substr(countBefore, str.length() - countBefore - countAfter);
|
|
|
|
|
2022-04-18 13:25:27 +02:00
|
|
|
return str;
|
2022-04-20 16:53:41 +02:00
|
|
|
}
|
|
|
|
|
2023-11-12 14:40:02 +01:00
|
|
|
std::optional<float> getPlusMinusKeywordResult(std::string source, float relative) {
|
2022-12-17 23:37:44 +01:00
|
|
|
try {
|
|
|
|
return relative + stof(source);
|
|
|
|
} catch (...) {
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source);
|
2023-11-12 14:40:02 +01:00
|
|
|
return {};
|
2022-04-20 16:53:41 +02:00
|
|
|
}
|
2022-05-05 12:50:25 +02:00
|
|
|
}
|
|
|
|
|
2022-06-26 22:15:06 +02:00
|
|
|
bool isNumber(const std::string& str, bool allowfloat) {
|
2022-09-28 16:32:53 +02:00
|
|
|
|
|
|
|
std::string copy = str;
|
|
|
|
if (*copy.begin() == '-')
|
|
|
|
copy = copy.substr(1);
|
2022-10-10 01:35:42 +02:00
|
|
|
|
2022-09-30 22:54:13 +02:00
|
|
|
if (copy.empty())
|
|
|
|
return false;
|
2022-09-28 16:32:53 +02:00
|
|
|
|
|
|
|
bool point = !allowfloat;
|
|
|
|
for (auto& c : copy) {
|
|
|
|
if (c == '.') {
|
|
|
|
if (point)
|
|
|
|
return false;
|
|
|
|
point = true;
|
2023-05-16 18:43:04 +02:00
|
|
|
continue;
|
2022-09-28 16:32:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!std::isdigit(c))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2022-05-05 12:50:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isDirection(const std::string& arg) {
|
|
|
|
return arg == "l" || arg == "r" || arg == "u" || arg == "d" || arg == "t" || arg == "b";
|
2022-05-18 12:18:58 +02:00
|
|
|
}
|
|
|
|
|
2023-11-05 17:18:41 +01:00
|
|
|
bool isDirection(const char& arg) {
|
|
|
|
return arg == 'l' || arg == 'r' || arg == 'u' || arg == 'd' || arg == 't' || arg == 'b';
|
|
|
|
}
|
|
|
|
|
2022-05-18 12:18:58 +02:00
|
|
|
int getWorkspaceIDFromString(const std::string& in, std::string& outName) {
|
2023-11-12 14:34:42 +01:00
|
|
|
int result = WORKSPACE_INVALID;
|
2023-10-15 20:07:23 +02:00
|
|
|
if (in.starts_with("special")) {
|
2022-05-31 14:01:00 +02:00
|
|
|
outName = "special";
|
2022-11-27 23:42:22 +01:00
|
|
|
|
|
|
|
if (in.length() > 8) {
|
|
|
|
const auto NAME = in.substr(8);
|
|
|
|
|
|
|
|
const auto WS = g_pCompositor->getWorkspaceByName("special:" + NAME);
|
2022-12-16 18:17:31 +01:00
|
|
|
|
2022-11-27 23:42:22 +01:00
|
|
|
outName = "special:" + NAME;
|
|
|
|
|
|
|
|
return WS ? WS->m_iID : g_pCompositor->getNewSpecialID();
|
|
|
|
}
|
|
|
|
|
|
|
|
return SPECIAL_WORKSPACE_START;
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (in.starts_with("name:")) {
|
2022-05-18 12:18:58 +02:00
|
|
|
const auto WORKSPACENAME = in.substr(in.find_first_of(':') + 1);
|
2022-12-16 18:17:31 +01:00
|
|
|
const auto WORKSPACE = g_pCompositor->getWorkspaceByName(WORKSPACENAME);
|
2022-05-18 12:18:58 +02:00
|
|
|
if (!WORKSPACE) {
|
|
|
|
result = g_pCompositor->getNextAvailableNamedWorkspace();
|
|
|
|
} else {
|
|
|
|
result = WORKSPACE->m_iID;
|
|
|
|
}
|
|
|
|
outName = WORKSPACENAME;
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (in.starts_with("empty")) {
|
2022-11-23 15:10:26 +01:00
|
|
|
int id = 0;
|
|
|
|
while (++id < INT_MAX) {
|
|
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id);
|
|
|
|
if (!PWORKSPACE || (g_pCompositor->getWindowsOnWorkspace(id) == 0))
|
|
|
|
return id;
|
|
|
|
}
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (in.starts_with("prev")) {
|
2023-02-09 15:15:42 +01:00
|
|
|
if (!g_pCompositor->m_pLastMonitor)
|
2023-11-12 14:34:42 +01:00
|
|
|
return WORKSPACE_INVALID;
|
2023-02-09 15:15:42 +01:00
|
|
|
|
|
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
|
|
|
|
|
|
|
|
if (!PWORKSPACE)
|
2023-11-12 14:34:42 +01:00
|
|
|
return WORKSPACE_INVALID;
|
2023-02-09 15:15:42 +01:00
|
|
|
|
2023-03-13 16:19:25 +01:00
|
|
|
const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->m_sPrevWorkspace.iID);
|
2023-02-09 15:15:42 +01:00
|
|
|
|
|
|
|
if (!PLASTWORKSPACE)
|
2023-11-12 14:34:42 +01:00
|
|
|
return WORKSPACE_INVALID;
|
2023-02-09 15:15:42 +01:00
|
|
|
|
|
|
|
outName = PLASTWORKSPACE->m_szName;
|
|
|
|
return PLASTWORKSPACE->m_iID;
|
2022-05-18 12:18:58 +02:00
|
|
|
} else {
|
2023-05-08 15:37:59 +02:00
|
|
|
if (in[0] == 'r' && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
|
|
|
|
if (!g_pCompositor->m_pLastMonitor) {
|
|
|
|
Debug::log(ERR, "Relative monitor workspace on monitor null!");
|
2023-11-12 14:34:42 +01:00
|
|
|
return WORKSPACE_INVALID;
|
2023-05-08 15:37:59 +02:00
|
|
|
}
|
2023-11-12 14:40:02 +01:00
|
|
|
|
|
|
|
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in.substr(1), 0);
|
|
|
|
|
|
|
|
if (!PLUSMINUSRESULT.has_value())
|
|
|
|
return WORKSPACE_INVALID;
|
|
|
|
|
|
|
|
result = (int)PLUSMINUSRESULT.value();
|
2023-05-08 15:37:59 +02:00
|
|
|
|
|
|
|
int remains = (int)result;
|
|
|
|
|
|
|
|
std::set<int> invalidWSes;
|
|
|
|
|
|
|
|
// Collect all the workspaces we can't jump to.
|
|
|
|
for (auto& ws : g_pCompositor->m_vWorkspaces) {
|
|
|
|
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID)) {
|
|
|
|
// Can't jump to this workspace
|
|
|
|
invalidWSes.insert(ws->m_iID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto& rule : g_pConfigManager->getAllWorkspaceRules()) {
|
|
|
|
const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor);
|
|
|
|
if (!PMONITOR || PMONITOR->ID == g_pCompositor->m_pLastMonitor->ID) {
|
|
|
|
// Can't be invalid
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// WS is bound to another monitor, can't jump to this
|
|
|
|
invalidWSes.insert(rule.workspaceId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare all named workspaces in case when we need them
|
|
|
|
std::vector<int> namedWSes;
|
|
|
|
for (auto& ws : g_pCompositor->m_vWorkspaces) {
|
|
|
|
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID) || ws->m_iID >= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
namedWSes.push_back(ws->m_iID);
|
|
|
|
}
|
|
|
|
std::sort(namedWSes.begin(), namedWSes.end());
|
|
|
|
|
|
|
|
// Just take a blind guess at where we'll probably end up
|
|
|
|
int predictedWSID = g_pCompositor->m_pLastMonitor->activeWorkspace + remains;
|
|
|
|
int remainingWSes = 0;
|
|
|
|
char walkDir = in[1];
|
|
|
|
|
|
|
|
// sanitize. 0 means invalid oob in -
|
|
|
|
predictedWSID = std::max(predictedWSID, 0);
|
|
|
|
|
|
|
|
// Count how many invalidWSes are in between (how bad the prediction was)
|
|
|
|
int beginID = in[1] == '+' ? g_pCompositor->m_pLastMonitor->activeWorkspace + 1 : predictedWSID;
|
|
|
|
int endID = in[1] == '+' ? predictedWSID : g_pCompositor->m_pLastMonitor->activeWorkspace;
|
|
|
|
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
|
|
|
|
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) {
|
|
|
|
remainingWSes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle named workspaces. They are treated like always before other workspaces
|
|
|
|
if (g_pCompositor->m_pLastMonitor->activeWorkspace < 0) {
|
|
|
|
// Behaviour similar to 'm'
|
|
|
|
// Find current
|
|
|
|
int currentItem = -1;
|
|
|
|
for (size_t i = 0; i < namedWSes.size(); i++) {
|
|
|
|
if (namedWSes[i] == g_pCompositor->m_pLastMonitor->activeWorkspace) {
|
|
|
|
currentItem = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
currentItem += remains;
|
|
|
|
currentItem = std::max(currentItem, 0);
|
|
|
|
if (currentItem >= (int)namedWSes.size()) {
|
|
|
|
// At the seam between namedWSes and normal WSes. Behave like r+[diff] at imaginary ws 0
|
|
|
|
int diff = currentItem - (namedWSes.size() - 1);
|
|
|
|
predictedWSID = diff;
|
|
|
|
int beginID = 1;
|
|
|
|
int endID = predictedWSID;
|
|
|
|
auto begin = invalidWSes.upper_bound(beginID - 1); // upper_bound is >, we want >=
|
|
|
|
for (auto it = begin; *it <= endID && it != invalidWSes.end(); it++) {
|
|
|
|
remainingWSes++;
|
|
|
|
}
|
|
|
|
walkDir = '+';
|
|
|
|
} else {
|
|
|
|
// We found our final ws.
|
|
|
|
remainingWSes = 0;
|
|
|
|
predictedWSID = namedWSes[currentItem];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go in the search direction for remainingWSes
|
|
|
|
// The performance impact is directly proportional to the number of open and bound workspaces
|
|
|
|
int finalWSID = predictedWSID;
|
|
|
|
if (walkDir == '-') {
|
|
|
|
int beginID = finalWSID;
|
|
|
|
int curID = finalWSID;
|
|
|
|
while (--curID > 0 && remainingWSes > 0) {
|
2023-10-15 20:07:23 +02:00
|
|
|
if (!invalidWSes.contains(curID)) {
|
2023-05-08 15:37:59 +02:00
|
|
|
remainingWSes--;
|
|
|
|
}
|
|
|
|
finalWSID = curID;
|
|
|
|
}
|
2023-10-15 20:07:23 +02:00
|
|
|
if (finalWSID <= 0 || invalidWSes.contains(finalWSID)) {
|
2023-05-08 15:37:59 +02:00
|
|
|
if (namedWSes.size()) {
|
|
|
|
// Go to the named workspaces
|
|
|
|
// Need remainingWSes more
|
|
|
|
int namedWSIdx = namedWSes.size() - remainingWSes;
|
|
|
|
// Sanitze
|
|
|
|
namedWSIdx = std::clamp(namedWSIdx, 0, (int)namedWSes.size() - 1);
|
|
|
|
finalWSID = namedWSes[namedWSIdx];
|
|
|
|
} else {
|
|
|
|
// Couldn't find valid workspace in negative direction, search last first one back up positive direction
|
|
|
|
walkDir = '+';
|
|
|
|
// We know, that everything less than beginID is invalid, so don't bother with that
|
|
|
|
finalWSID = beginID;
|
|
|
|
remainingWSes = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (walkDir == '+') {
|
|
|
|
int curID = finalWSID;
|
|
|
|
while (++curID < INT32_MAX && remainingWSes > 0) {
|
2023-10-15 20:07:23 +02:00
|
|
|
if (!invalidWSes.contains(curID)) {
|
2023-05-08 15:37:59 +02:00
|
|
|
remainingWSes--;
|
|
|
|
}
|
|
|
|
finalWSID = curID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result = finalWSID;
|
|
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(result);
|
|
|
|
if (PWORKSPACE)
|
|
|
|
outName = g_pCompositor->getWorkspaceByID(result)->m_szName;
|
|
|
|
else
|
|
|
|
outName = std::to_string(finalWSID);
|
|
|
|
|
|
|
|
} else if ((in[0] == 'm' || in[0] == 'e') && (in[1] == '-' || in[1] == '+') && isNumber(in.substr(2))) {
|
2022-07-16 14:28:17 +02:00
|
|
|
bool onAllMonitors = in[0] == 'e';
|
|
|
|
|
2022-06-21 22:42:54 +02:00
|
|
|
if (!g_pCompositor->m_pLastMonitor) {
|
|
|
|
Debug::log(ERR, "Relative monitor workspace on monitor null!");
|
2023-11-12 14:34:42 +01:00
|
|
|
return WORKSPACE_INVALID;
|
2022-06-21 22:42:54 +02:00
|
|
|
}
|
|
|
|
|
2022-05-31 12:33:08 +02:00
|
|
|
// monitor relative
|
2023-11-12 14:40:02 +01:00
|
|
|
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in.substr(1), 0);
|
|
|
|
|
|
|
|
if (!PLUSMINUSRESULT.has_value())
|
|
|
|
return WORKSPACE_INVALID;
|
|
|
|
|
|
|
|
result = (int)PLUSMINUSRESULT.value();
|
2022-05-31 12:33:08 +02:00
|
|
|
|
|
|
|
// result now has +/- what we should move on mon
|
2022-12-16 18:17:31 +01:00
|
|
|
int remains = (int)result;
|
|
|
|
|
2022-12-08 13:08:41 +01:00
|
|
|
std::vector<int> validWSes;
|
|
|
|
for (auto& ws : g_pCompositor->m_vWorkspaces) {
|
|
|
|
if (ws->m_bIsSpecialWorkspace || (ws->m_iMonitorID != g_pCompositor->m_pLastMonitor->ID && !onAllMonitors))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
validWSes.push_back(ws->m_iID);
|
|
|
|
}
|
2022-05-31 12:33:08 +02:00
|
|
|
|
2022-12-08 22:24:55 +01:00
|
|
|
std::sort(validWSes.begin(), validWSes.end());
|
|
|
|
|
2022-12-08 13:08:41 +01:00
|
|
|
// get the offset
|
|
|
|
remains = remains < 0 ? -((-remains) % validWSes.size()) : remains % validWSes.size();
|
2022-05-31 12:33:08 +02:00
|
|
|
|
2022-12-08 13:08:41 +01:00
|
|
|
// get the current item
|
|
|
|
int currentItem = -1;
|
|
|
|
for (size_t i = 0; i < validWSes.size(); i++) {
|
|
|
|
if (validWSes[i] == g_pCompositor->m_pLastMonitor->activeWorkspace) {
|
|
|
|
currentItem = i;
|
|
|
|
break;
|
2022-05-31 12:33:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 13:08:41 +01:00
|
|
|
// apply
|
|
|
|
currentItem += remains;
|
|
|
|
|
|
|
|
// sanitize
|
|
|
|
if (currentItem >= (int)validWSes.size()) {
|
|
|
|
currentItem = currentItem % validWSes.size();
|
|
|
|
} else if (currentItem < 0) {
|
|
|
|
currentItem = validWSes.size() + currentItem;
|
|
|
|
}
|
|
|
|
|
2022-12-16 18:17:31 +01:00
|
|
|
result = validWSes[currentItem];
|
2022-12-08 13:08:41 +01:00
|
|
|
outName = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_szName;
|
2022-05-31 12:33:08 +02:00
|
|
|
} else {
|
2022-09-28 16:26:41 +02:00
|
|
|
if (in[0] == '+' || in[0] == '-') {
|
2023-11-12 14:40:02 +01:00
|
|
|
if (g_pCompositor->m_pLastMonitor) {
|
|
|
|
const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspace);
|
|
|
|
if (!PLUSMINUSRESULT.has_value())
|
|
|
|
return WORKSPACE_INVALID;
|
|
|
|
|
|
|
|
result = std::max((int)PLUSMINUSRESULT.value(), 1);
|
|
|
|
} else {
|
2022-09-28 16:26:41 +02:00
|
|
|
Debug::log(ERR, "Relative workspace on no mon!");
|
2023-11-12 14:34:42 +01:00
|
|
|
return WORKSPACE_INVALID;
|
2022-09-28 16:26:41 +02:00
|
|
|
}
|
|
|
|
} else if (isNumber(in))
|
2022-09-26 21:10:24 +02:00
|
|
|
result = std::max(std::stoi(in), 1);
|
2022-06-21 22:42:54 +02:00
|
|
|
else {
|
2022-09-28 16:26:41 +02:00
|
|
|
// maybe name
|
|
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByName(in);
|
|
|
|
if (PWORKSPACE)
|
|
|
|
result = PWORKSPACE->m_iID;
|
2022-06-21 22:42:54 +02:00
|
|
|
}
|
2022-09-28 19:43:35 +02:00
|
|
|
|
2022-05-31 12:33:08 +02:00
|
|
|
outName = std::to_string(result);
|
2022-09-25 20:07:48 +02:00
|
|
|
}
|
2022-05-18 12:18:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2022-05-25 18:40:03 +02:00
|
|
|
}
|
|
|
|
|
2023-10-23 00:11:03 +02:00
|
|
|
std::optional<std::string> cleanCmdForWorkspace(const std::string& inWorkspaceName, std::string dirtyCmd) {
|
|
|
|
|
|
|
|
std::string cmd = removeBeginEndSpacesTabs(dirtyCmd);
|
|
|
|
|
|
|
|
if (!cmd.empty()) {
|
|
|
|
std::string rules;
|
|
|
|
const std::string workspaceRule = "workspace " + inWorkspaceName;
|
|
|
|
|
|
|
|
if (cmd[0] == '[') {
|
|
|
|
const int closingBracketIdx = cmd.find_last_of(']');
|
|
|
|
auto tmpRules = cmd.substr(1, closingBracketIdx - 1);
|
|
|
|
cmd = cmd.substr(closingBracketIdx + 1);
|
|
|
|
|
|
|
|
auto rulesList = CVarList(tmpRules, 0, ';');
|
|
|
|
|
|
|
|
bool hadWorkspaceRule = false;
|
|
|
|
rulesList.map([&](std::string& rule) {
|
|
|
|
if (rule.find("workspace") == 0) {
|
|
|
|
rule = workspaceRule;
|
|
|
|
hadWorkspaceRule = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!hadWorkspaceRule)
|
|
|
|
rulesList.append(workspaceRule);
|
|
|
|
|
|
|
|
rules = "[" + rulesList.join(";") + "]";
|
|
|
|
} else {
|
|
|
|
rules = "[" + workspaceRule + "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::optional<std::string>(rules + " " + cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2022-05-25 18:40:03 +02:00
|
|
|
float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2) {
|
2022-09-28 19:43:35 +02:00
|
|
|
const float DX = std::max({0.0, p1.x - vec.x, vec.x - p2.x});
|
|
|
|
const float DY = std::max({0.0, p1.y - vec.y, vec.y - p2.y});
|
2022-05-25 18:40:03 +02:00
|
|
|
return DX * DX + DY * DY;
|
2022-06-25 20:49:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute a shell command and get the output
|
|
|
|
std::string execAndGet(const char* cmd) {
|
2022-12-16 18:17:31 +01:00
|
|
|
std::array<char, 128> buffer;
|
|
|
|
std::string result;
|
2022-06-25 20:49:06 +02:00
|
|
|
const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
|
|
|
|
if (!pipe) {
|
|
|
|
Debug::log(ERR, "execAndGet: failed in pipe");
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
|
|
|
|
result += buffer.data();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void logSystemInfo() {
|
|
|
|
struct utsname unameInfo;
|
|
|
|
|
|
|
|
uname(&unameInfo);
|
|
|
|
|
2023-12-19 00:33:10 +01:00
|
|
|
Debug::log(LOG, "System name: {}", std::string{unameInfo.sysname});
|
|
|
|
Debug::log(LOG, "Node name: {}", std::string{unameInfo.nodename});
|
|
|
|
Debug::log(LOG, "Release: {}", std::string{unameInfo.release});
|
|
|
|
Debug::log(LOG, "Version: {}", std::string{unameInfo.version});
|
2022-10-10 01:35:42 +02:00
|
|
|
|
2022-10-04 00:10:15 +02:00
|
|
|
Debug::log(NONE, "\n");
|
|
|
|
|
2023-02-20 15:15:15 +01:00
|
|
|
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
|
|
|
const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga");
|
|
|
|
#else
|
2022-10-04 00:10:15 +02:00
|
|
|
const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA");
|
2023-02-20 15:15:15 +01:00
|
|
|
#endif
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(LOG, "GPU information:\n{}\n", GPUINFO);
|
2022-10-04 00:10:15 +02:00
|
|
|
|
|
|
|
if (GPUINFO.contains("NVIDIA")) {
|
|
|
|
Debug::log(WARN, "Warning: you're using an NVIDIA GPU. Make sure you follow the instructions on the wiki if anything is amiss.\n");
|
|
|
|
}
|
2022-06-25 20:49:06 +02:00
|
|
|
|
|
|
|
// log etc
|
|
|
|
Debug::log(LOG, "os-release:");
|
|
|
|
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(NONE, "{}", execAndGet("cat /etc/os-release"));
|
2022-06-28 11:30:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void matrixProjection(float mat[9], int w, int h, wl_output_transform tr) {
|
|
|
|
memset(mat, 0, sizeof(*mat) * 9);
|
|
|
|
|
|
|
|
const float* t = transforms[tr];
|
2022-12-16 18:17:31 +01:00
|
|
|
float x = 2.0f / w;
|
|
|
|
float y = 2.0f / h;
|
2022-06-28 11:30:07 +02:00
|
|
|
|
|
|
|
// Rotation + reflection
|
|
|
|
mat[0] = x * t[0];
|
|
|
|
mat[1] = x * t[1];
|
2022-10-10 01:35:42 +02:00
|
|
|
mat[3] = y * t[3];
|
|
|
|
mat[4] = y * t[4];
|
2022-06-28 11:30:07 +02:00
|
|
|
|
|
|
|
// Translation
|
|
|
|
mat[2] = -copysign(1.0f, mat[0] + mat[1]);
|
|
|
|
mat[5] = -copysign(1.0f, mat[3] + mat[4]);
|
|
|
|
|
|
|
|
// Identity
|
|
|
|
mat[8] = 1.0f;
|
|
|
|
}
|
2022-10-01 20:19:15 +02:00
|
|
|
|
|
|
|
int64_t getPPIDof(int64_t pid) {
|
2022-11-07 21:26:23 +01:00
|
|
|
#if defined(KERN_PROC_PID)
|
|
|
|
int mib[] = {
|
|
|
|
CTL_KERN,
|
|
|
|
KERN_PROC,
|
|
|
|
KERN_PROC_PID,
|
|
|
|
(int)pid,
|
2022-12-16 18:17:31 +01:00
|
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
2022-11-07 21:26:23 +01:00
|
|
|
sizeof(KINFO_PROC),
|
|
|
|
1,
|
2022-12-16 18:17:31 +01:00
|
|
|
#endif
|
2022-11-07 21:26:23 +01:00
|
|
|
};
|
2022-12-16 18:17:31 +01:00
|
|
|
u_int miblen = sizeof(mib) / sizeof(mib[0]);
|
2022-11-07 21:26:23 +01:00
|
|
|
KINFO_PROC kp;
|
2022-12-16 18:17:31 +01:00
|
|
|
size_t sz = sizeof(KINFO_PROC);
|
2022-11-07 21:26:23 +01:00
|
|
|
if (sysctl(mib, miblen, &kp, &sz, NULL, 0) != -1)
|
|
|
|
return KP_PPID(kp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
#else
|
2023-10-23 00:11:03 +02:00
|
|
|
std::string dir = "/proc/" + std::to_string(pid) + "/status";
|
|
|
|
FILE* infile;
|
2022-10-01 20:19:15 +02:00
|
|
|
|
|
|
|
infile = fopen(dir.c_str(), "r");
|
|
|
|
if (!infile)
|
|
|
|
return 0;
|
|
|
|
|
2022-12-16 18:17:31 +01:00
|
|
|
char* line = nullptr;
|
|
|
|
size_t len = 0;
|
|
|
|
ssize_t len2 = 0;
|
2022-10-01 20:19:15 +02:00
|
|
|
|
|
|
|
std::string pidstr;
|
|
|
|
|
|
|
|
while ((len2 = getline(&line, &len, infile)) != -1) {
|
|
|
|
if (strstr(line, "PPid:")) {
|
2022-12-16 18:17:31 +01:00
|
|
|
pidstr = std::string(line, len2);
|
2022-10-01 20:19:15 +02:00
|
|
|
const auto tabpos = pidstr.find_last_of('\t');
|
|
|
|
if (tabpos != std::string::npos)
|
|
|
|
pidstr = pidstr.substr(tabpos);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(infile);
|
|
|
|
if (line)
|
|
|
|
free(line);
|
|
|
|
|
|
|
|
try {
|
|
|
|
return std::stoll(pidstr);
|
2022-12-16 18:17:31 +01:00
|
|
|
} catch (std::exception& e) { return 0; }
|
2022-11-07 21:26:23 +01:00
|
|
|
#endif
|
2022-10-10 01:35:42 +02:00
|
|
|
}
|
2022-11-13 20:33:13 +01:00
|
|
|
|
|
|
|
int64_t configStringToInt(const std::string& VALUE) {
|
2023-10-15 20:07:23 +02:00
|
|
|
if (VALUE.starts_with("0x")) {
|
2022-11-13 20:33:13 +01:00
|
|
|
// Values with 0x are hex
|
|
|
|
const auto VALUEWITHOUTHEX = VALUE.substr(2);
|
|
|
|
return stol(VALUEWITHOUTHEX, nullptr, 16);
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
|
2022-11-13 20:33:13 +01:00
|
|
|
const auto VALUEWITHOUTFUNC = VALUE.substr(5, VALUE.length() - 6);
|
|
|
|
|
|
|
|
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 8) {
|
2023-09-06 12:51:36 +02:00
|
|
|
Debug::log(WARN, "invalid length {} for rgba", VALUEWITHOUTFUNC.length());
|
2022-11-13 20:33:13 +01:00
|
|
|
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes)");
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto RGBA = std::stol(VALUEWITHOUTFUNC, nullptr, 16);
|
|
|
|
|
|
|
|
// now we need to RGBA -> ARGB. The config holds ARGB only.
|
|
|
|
return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF);
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
|
2022-11-13 20:33:13 +01:00
|
|
|
const auto VALUEWITHOUTFUNC = VALUE.substr(4, VALUE.length() - 5);
|
|
|
|
|
|
|
|
if (removeBeginEndSpacesTabs(VALUEWITHOUTFUNC).length() != 6) {
|
2023-09-06 12:51:36 +02:00
|
|
|
Debug::log(WARN, "invalid length {} for rgb", VALUEWITHOUTFUNC.length());
|
2022-11-13 20:33:13 +01:00
|
|
|
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes)");
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto RGB = std::stol(VALUEWITHOUTFUNC, nullptr, 16);
|
|
|
|
|
|
|
|
return RGB + 0xFF000000; // 0xFF for opaque
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
|
2022-11-13 20:33:13 +01:00
|
|
|
return 1;
|
2023-10-15 20:07:23 +02:00
|
|
|
} else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
|
2022-11-13 20:33:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2023-11-18 00:29:30 +01:00
|
|
|
|
|
|
|
if (VALUE.empty() || !isNumber(VALUE))
|
|
|
|
return 0;
|
|
|
|
|
2023-01-24 22:44:54 +01:00
|
|
|
return std::stoll(VALUE);
|
2022-12-17 23:37:44 +01:00
|
|
|
}
|
2023-02-01 22:06:01 +01:00
|
|
|
|
|
|
|
double normalizeAngleRad(double ang) {
|
|
|
|
if (ang > M_PI * 2) {
|
|
|
|
while (ang > M_PI * 2)
|
|
|
|
ang -= M_PI * 2;
|
|
|
|
return ang;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ang < 0.0) {
|
|
|
|
while (ang < 0.0)
|
|
|
|
ang += M_PI * 2;
|
|
|
|
return ang;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ang;
|
2023-02-19 14:45:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string replaceInString(std::string subject, const std::string& search, const std::string& replace) {
|
|
|
|
size_t pos = 0;
|
|
|
|
while ((pos = subject.find(search, pos)) != std::string::npos) {
|
|
|
|
subject.replace(pos, search.length(), replace);
|
|
|
|
pos += replace.length();
|
|
|
|
}
|
|
|
|
return subject;
|
2023-03-05 19:16:42 +01:00
|
|
|
}
|
2023-08-11 17:22:16 +02:00
|
|
|
|
|
|
|
std::vector<SCallstackFrameInfo> getBacktrace() {
|
|
|
|
std::vector<SCallstackFrameInfo> callstack;
|
|
|
|
|
2023-10-11 23:27:53 +02:00
|
|
|
#ifdef HAS_EXECINFO
|
|
|
|
void* bt[1024];
|
|
|
|
size_t btSize;
|
|
|
|
char** btSymbols;
|
2023-08-11 17:22:16 +02:00
|
|
|
|
|
|
|
btSize = backtrace(bt, 1024);
|
|
|
|
btSymbols = backtrace_symbols(bt, btSize);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < btSize; ++i) {
|
|
|
|
callstack.emplace_back(SCallstackFrameInfo{bt[i], std::string{btSymbols[i]}});
|
|
|
|
}
|
2023-10-11 23:27:53 +02:00
|
|
|
#else
|
|
|
|
callstack.emplace_back(SCallstackFrameInfo{nullptr, "configuration does not support execinfo.h"});
|
|
|
|
#endif
|
2023-08-11 17:22:16 +02:00
|
|
|
|
|
|
|
return callstack;
|
|
|
|
}
|
2023-08-20 11:47:06 +02:00
|
|
|
|
|
|
|
void throwError(const std::string& err) {
|
2023-09-06 21:45:37 +02:00
|
|
|
Debug::log(CRIT, "Critical error thrown: {}", err);
|
2023-08-20 11:47:06 +02:00
|
|
|
throw std::runtime_error(err);
|
2023-10-30 16:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t drmFormatToGL(uint32_t drm) {
|
|
|
|
switch (drm) {
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
|
|
|
case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case.
|
|
|
|
case DRM_FORMAT_XRGB2101010:
|
2023-11-03 16:47:20 +01:00
|
|
|
case DRM_FORMAT_XBGR2101010:
|
|
|
|
#ifdef GLES2
|
|
|
|
return GL_RGB10_A2_EXT;
|
|
|
|
#else
|
|
|
|
return GL_RGB10_A2;
|
|
|
|
#endif
|
2023-10-30 16:56:02 +01:00
|
|
|
default: return GL_RGBA;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return GL_RGBA;
|
2023-12-01 18:20:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t glFormatToType(uint32_t gl) {
|
|
|
|
return gl != GL_RGBA ?
|
|
|
|
#ifdef GLES2
|
|
|
|
GL_UNSIGNED_INT_2_10_10_10_REV_EXT :
|
|
|
|
#else
|
|
|
|
GL_UNSIGNED_INT_2_10_10_10_REV :
|
|
|
|
#endif
|
|
|
|
GL_UNSIGNED_BYTE;
|
2023-12-21 22:27:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool envEnabled(const std::string& env) {
|
|
|
|
const auto ENV = getenv(env.c_str());
|
|
|
|
if (!ENV)
|
|
|
|
return false;
|
|
|
|
return std::string(ENV) == "1";
|
2023-09-20 09:26:20 +02:00
|
|
|
}
|