Skip to content

Commit

Permalink
Added process path context filter
Browse files Browse the repository at this point in the history
  • Loading branch information
houmain committed Aug 8, 2023
1 parent ee080f1 commit fc18989
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/client/FocusedWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class FocusedWindow {
bool update();
const std::string& window_class() const;
const std::string& window_title() const;
const std::string& window_path() const;
bool is_inaccessible() const;

private:
Expand Down
15 changes: 15 additions & 0 deletions src/client/linux/FocusedWindowImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,18 @@ const std::string& FocusedWindow::window_class() const {
const std::string& FocusedWindow::window_title() const {
return m_impl->window_title;
}

const std::string& FocusedWindow::window_path() const {
return m_impl->window_path;
}

//-------------------------------------------------------------------------

std::string get_process_path_by_pid(int pid) {
char path[32];
std::sprintf(path, "/proc/%d/exe", pid);
const auto resolved = ::realpath(path, nullptr);
auto result = std::string(resolved);
::free(resolved);
return result;
}
3 changes: 3 additions & 0 deletions src/client/linux/FocusedWindowImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
struct FocusedWindowData {
std::string window_class;
std::string window_title;
std::string window_path;
};

class FocusedWindowSystem {
Expand All @@ -24,3 +25,5 @@ class FocusedWindowImpl : public FocusedWindowData {
void shutdown();
bool update();
};

std::string get_process_path_by_pid(int pid);
24 changes: 23 additions & 1 deletion src/client/linux/FocusedWindowX11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class FocusedWindowX11 : public FocusedWindowSystem {
Window m_root_window{ };
Atom m_net_active_window_atom{ };
Atom m_net_wm_name_atom{ };
Atom m_net_wm_pid_atom{ };
Atom m_utf8_string_atom{ };
Window m_focused_window{ };

Expand All @@ -39,6 +40,7 @@ class FocusedWindowX11 : public FocusedWindowSystem {
m_root_window = XRootWindow(m_display, 0);
m_net_active_window_atom = XInternAtom(m_display, "_NET_ACTIVE_WINDOW", False);
m_net_wm_name_atom = XInternAtom(m_display, "_NET_WM_NAME", False);
m_net_wm_pid_atom = XInternAtom(m_display, "_NET_WM_PID", False);
m_utf8_string_atom = XInternAtom(m_display, "UTF8_STRING", False);
XSetErrorHandler([](Display*, XErrorEvent*) { return 0; });
return true;
Expand All @@ -53,12 +55,14 @@ class FocusedWindowX11 : public FocusedWindowSystem {

// window handles can become invalid any time
auto window_class = get_window_class(window);
if (window_class.empty() || window_title.empty())
auto window_path = get_window_path(window);
if (window_class.empty() || window_title.empty() || window_path.empty())
return false;

m_focused_window = window;
m_data.window_class = std::move(window_class);
m_data.window_title = std::move(window_title);
m_data.window_path = std::move(window_path);
return true;
}

Expand Down Expand Up @@ -109,6 +113,24 @@ class FocusedWindowX11 : public FocusedWindowSystem {
}
return { };
}

std::string get_window_path(Window window) {
auto type = Atom{ };
auto format = 0;
auto length = 0ul;
auto rest = 0ul;
auto data = std::add_pointer_t<unsigned char>{ };
if (window &&
XGetWindowProperty(m_display, window, m_net_wm_pid_atom, 0, 1,
False, XA_CARDINAL, &type, &format, &length,
&rest, &data) == Success &&
data) {
const auto pid = *reinterpret_cast<unsigned long*>(data);
XFree(data);
return get_process_path_by_pid(pid);
}
return { };
}
};

std::unique_ptr<FocusedWindowSystem> make_focused_window_x11(FocusedWindowData* data) {
Expand Down
4 changes: 3 additions & 1 deletion src/client/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ namespace {
const auto& contexts = g_config_file.config().contexts;
const auto& window_class = g_focused_window.window_class();
const auto& window_title = g_focused_window.window_title();
const auto& window_path = g_focused_window.window_path();
for (auto i = 0; i < static_cast<int>(contexts.size()); ++i)
if (contexts[i].matches(window_class, window_title))
if (contexts[i].matches(window_class, window_title, window_path))
g_active_contexts.push_back(i);

return g_server.send_active_contexts(g_active_contexts);
Expand Down Expand Up @@ -85,6 +86,7 @@ namespace {
verbose("Detected focused window changed:");
verbose(" class = '%s'", g_focused_window.window_class().c_str());
verbose(" title = '%s'", g_focused_window.window_title().c_str());
verbose(" path = '%s'", g_focused_window.window_path().c_str());
if (!send_active_contexts())
return;
}
Expand Down
20 changes: 20 additions & 0 deletions src/client/windows/FocusedWindow.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#include "client/FocusedWindow.h"
#include "common/windows/win.h"
#include <psapi.h>
#include <array>
#include <cstring>

Expand All @@ -25,11 +26,13 @@ class FocusedWindowImpl {
std::wstring m_current_title;
std::string m_class;
std::string m_title;
std::string m_path;

public:
HWND current() const { return m_current_window; }
const std::string& window_class() const { return m_class; }
const std::string& window_title() const { return m_title; }
const std::string& window_path() const { return m_path; }

bool update() {
const auto hwnd = GetForegroundWindow();
Expand All @@ -50,6 +53,19 @@ class FocusedWindowImpl {
GetClassNameW(hwnd, buffer.data(), static_cast<int>(buffer.size()));
m_class = wide_to_utf8(buffer.data());
m_title = wide_to_utf8(m_current_title);

#if (PSAPI_VERSION >= 2)
m_path.clear();
auto pid = DWORD{ };
auto buffer_size = DWORD{ buffer.size() };
if (GetWindowThreadProcessId(hwnd, &pid))
if (const auto handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)) {
if (QueryFullProcessImageNameW(handle, 0, buffer.data(), &buffer_size))
m_path = wide_to_utf8(buffer.data());
CloseHandle(handle);
}
#endif

return true;
}
};
Expand All @@ -74,6 +90,10 @@ const std::string& FocusedWindow::window_title() const {
return m_impl->window_title();
}

const std::string& FocusedWindow::window_path() const {
return m_impl->window_path();
}

bool FocusedWindow::is_inaccessible() const {
if (auto hwnd = m_impl->current()) {
auto process_id = DWORD{ };
Expand Down
7 changes: 5 additions & 2 deletions src/client/windows/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ namespace {
g_new_active_contexts.clear();
if (g_active)
for (auto i = 0; i < static_cast<int>(contexts.size()); ++i)
if (contexts[i].matches(g_focused_window.window_class(),
g_focused_window.window_title()))
if (contexts[i].matches(
g_focused_window.window_class(),
g_focused_window.window_title(),
g_focused_window.window_path()))
g_new_active_contexts.push_back(i);

if (force_send || g_new_active_contexts != g_current_active_contexts) {
Expand Down Expand Up @@ -139,6 +141,7 @@ namespace {
verbose("Detected focused window changed:");
verbose(" class = '%s'", g_focused_window.window_class().c_str());
verbose(" title = '%s'", g_focused_window.window_title().c_str());
verbose(" path = '%s'", g_focused_window.window_path().c_str());
update_active_contexts(false);
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/config/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ struct Config {
bool system_filter_matched;
Filter window_class_filter;
Filter window_title_filter;
Filter window_path_filter;
std::string device_filter;
std::vector<Input> inputs;
std::vector<KeySequence> outputs;
std::vector<CommandOutput> command_outputs;

bool matches(const std::string& window_class,
const std::string& window_title) const {
const std::string& window_title,
const std::string& window_path) const {
if (!window_class_filter.matches(window_class, false))
return false;
if (!window_title_filter.matches(window_title, true))
return false;
if (!window_path_filter.matches(window_path, true))
return false;
return true;
}
};
Expand Down
5 changes: 5 additions & 0 deletions src/config/ParseConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ void ParseConfig::parse_context(It* it, const It end) {
auto system_filter_matched = true;
auto class_filter = Config::Filter();
auto title_filter = Config::Filter();
auto path_filter = Config::Filter();
auto device_filter = std::string();
m_context_modifier = { };

Expand All @@ -213,6 +214,9 @@ void ParseConfig::parse_context(It* it, const It end) {
else if (attrib == "title") {
title_filter = read_filter(it, end);
}
else if (attrib == "path") {
path_filter = read_filter(it, end);
}
else if (attrib == "system") {
system_filter_matched =
(to_lower(read_value(it, end)) == current_system);
Expand Down Expand Up @@ -250,6 +254,7 @@ void ParseConfig::parse_context(It* it, const It end) {
system_filter_matched,
std::move(class_filter),
std::move(title_filter),
std::move(path_filter),
std::move(device_filter)
});
}
Expand Down
7 changes: 5 additions & 2 deletions src/test/test1_ParseConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ namespace {
return parse(stream);
}

int find_context(const Config& config, const char* window_class, const char* window_title) {
int find_context(const Config& config,
const char* window_class,
const char* window_title,
const char* window_path = "") {
const auto& contexts = config.contexts;
// skip default context
const auto begin = std::next(contexts.begin());
const auto end = contexts.end();
const auto it = std::find_if(begin, end,
[&](const Config::Context& context) {
return context.matches(window_class, window_title);
return context.matches(window_class, window_title, window_path);
});
return (it == end ? 0 : std::distance(begin, it) + 1);
}
Expand Down

0 comments on commit fc18989

Please sign in to comment.