Improve focus handling

* Display current focus window / process
 * Match against app name instead of window name on MacOS when using
   Process condition
 * Clean up
This commit is contained in:
WarmUpTill 2022-11-03 20:16:13 +01:00 committed by WarmUpTill
parent 267ab6a7c1
commit 50e26aba72
11 changed files with 167 additions and 31 deletions

View File

@ -128,6 +128,7 @@ AdvSceneSwitcher.condition.scene.entry.line2="{{useTransitionTargetScene}}"
AdvSceneSwitcher.condition.window="Window"
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
AdvSceneSwitcher.condition.window.entry.line3="Current foreground window: {{focusWindow}}"
AdvSceneSwitcher.condition.file="File"
AdvSceneSwitcher.condition.file.type.match="matches"
AdvSceneSwitcher.condition.file.type.contentChange="content changed"
@ -200,6 +201,7 @@ AdvSceneSwitcher.condition.record.state.stop="Recording stopped"
AdvSceneSwitcher.condition.record.entry="{{recordState}}"
AdvSceneSwitcher.condition.process="Process"
AdvSceneSwitcher.condition.process.entry="{{processes}} is running {{focused}} and is focused"
AdvSceneSwitcher.condition.process.entry.focus="Current foreground process: {{focusProcess}}"
AdvSceneSwitcher.condition.idle="Idle"
AdvSceneSwitcher.condition.idle.entry="No keyboard or mouse inputs for {{duration}}"
AdvSceneSwitcher.condition.pluginState="Plugin state"

View File

@ -280,6 +280,9 @@ void SwitcherData::setPreconditions()
}
currentTitle = title;
// Process name
GetForegroundProcessName(currentForegroundProcess);
// Cursor
std::pair<int, int> cursorPos = getCursorPos();
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||

View File

@ -434,10 +434,24 @@ std::string getProcNameFromPid(int pid)
return buffer.str();
}
bool isInFocus(const QString &executable)
void GetForegroundProcessName(QString &proc)
{
std::string temp;
GetForegroundProcessName(temp);
proc = QString::fromStdString(temp);
}
void GetForegroundProcessName(std::string &proc)
{
proc.resize(0);
auto pid = getForegroundProcessPid();
std::string current = getProcNameFromPid(pid);
}
bool isInFocus(const QString &executable)
{
std::string current;
GetForegroundProcessName(current);
// True if executable switch equals current window
bool equals = (executable.toStdString() == current);

View File

@ -48,36 +48,48 @@ std::string MacroConditionProcess::GetShortDesc()
MacroConditionProcessEdit::MacroConditionProcessEdit(
QWidget *parent, std::shared_ptr<MacroConditionProcess> entryData)
: QWidget(parent)
: QWidget(parent),
_processSelection(new QComboBox()),
_focused(new QCheckBox()),
_focusProcess(new QLabel()),
_focusLayout(new QHBoxLayout())
{
_processSelection = new QComboBox();
_processSelection->setEditable(true);
_processSelection->setMaxVisibleItems(20);
_focused = new QCheckBox();
QWidget::connect(_processSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(ProcessChanged(const QString &)));
QWidget::connect(_focused, SIGNAL(stateChanged(int)), this,
SLOT(FocusChanged(int)));
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateFocusProcess()));
populateProcessSelection(_processSelection);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{processes}}", _processSelection},
{"{{focused}}", _focused},
{"{{focusProcess}}", _focusProcess},
};
QHBoxLayout *mainLayout = new QHBoxLayout;
auto entryLayout = new QHBoxLayout;
placeWidgets(
obs_module_text("AdvSceneSwitcher.condition.process.entry"),
mainLayout, widgetPlaceholders);
entryLayout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.process.entry.focus"),
_focusLayout, widgetPlaceholders);
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addLayout(_focusLayout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
_timer.start(1000);
}
void MacroConditionProcessEdit::ProcessChanged(const QString &text)
@ -100,6 +112,22 @@ void MacroConditionProcessEdit::FocusChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_focus = state;
SetWidgetVisibility();
}
void MacroConditionProcessEdit::UpdateFocusProcess()
{
_focusProcess->setText(
QString::fromStdString(switcher->currentForegroundProcess));
}
void MacroConditionProcessEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
setLayoutVisible(_focusLayout, _entryData->_focus);
adjustSize();
}
void MacroConditionProcessEdit::UpdateEntryData()
@ -110,4 +138,5 @@ void MacroConditionProcessEdit::UpdateEntryData()
_processSelection->setCurrentText(_entryData->_process.c_str());
_focused->setChecked(_entryData->_focus);
SetWidgetVisibility();
}

View File

@ -44,14 +44,20 @@ public:
private slots:
void ProcessChanged(const QString &text);
void FocusChanged(int state);
void UpdateFocusProcess();
signals:
void HeaderInfoChanged(const QString &);
protected:
QComboBox *_processSelection;
QCheckBox *_focused;
QLabel *_focusProcess;
QHBoxLayout *_focusLayout;
QTimer _timer;
std::shared_ptr<MacroConditionProcess> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@ -13,7 +13,7 @@ bool MacroConditionWindow::_registered = MacroConditionFactory::Register(
"AdvSceneSwitcher.condition.window"});
bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
std::string &currentWindowTitle)
const std::string &currentWindowTitle)
{
bool focus = (!_focus || _window == currentWindowTitle);
bool fullscreen = (!_fullscreen || isFullscreen(_window));
@ -23,7 +23,8 @@ bool MacroConditionWindow::CheckWindowTitleSwitchDirect(
}
bool MacroConditionWindow::CheckWindowTitleSwitchRegex(
std::string &currentWindowTitle, std::vector<std::string> &windowList)
const std::string &currentWindowTitle,
const std::vector<std::string> &windowList)
{
bool match = false;
for (auto &window : windowList) {
@ -54,7 +55,7 @@ bool foregroundWindowChanged()
bool MacroConditionWindow::CheckCondition()
{
std::string currentWindowTitle = switcher->currentTitle;
const std::string &currentWindowTitle = switcher->currentTitle;
std::vector<std::string> windowList;
GetWindowList(windowList);
@ -101,17 +102,18 @@ std::string MacroConditionWindow::GetShortDesc()
MacroConditionWindowEdit::MacroConditionWindowEdit(
QWidget *parent, std::shared_ptr<MacroConditionWindow> entryData)
: QWidget(parent)
: QWidget(parent),
_windowSelection(new QComboBox()),
_fullscreen(new QCheckBox()),
_maximized(new QCheckBox()),
_focused(new QCheckBox()),
_windowFocusChanged(new QCheckBox()),
_focusWindow(new QLabel()),
_focusLayout(new QHBoxLayout())
{
_windowSelection = new QComboBox();
_windowSelection->setEditable(true);
_windowSelection->setMaxVisibleItems(20);
_fullscreen = new QCheckBox();
_maximized = new QCheckBox();
_focused = new QCheckBox();
_windowFocusChanged = new QCheckBox();
QWidget::connect(_windowSelection,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(WindowChanged(const QString &)));
@ -123,6 +125,8 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
SLOT(FocusedChanged(int)));
QWidget::connect(_windowFocusChanged, SIGNAL(stateChanged(int)), this,
SLOT(WindowFocusChanged(int)));
QWidget::connect(&_timer, SIGNAL(timeout()), this,
SLOT(UpdateFocusWindow()));
populateWindowSelection(_windowSelection);
@ -132,24 +136,31 @@ MacroConditionWindowEdit::MacroConditionWindowEdit(
{"{{maximized}}", _maximized},
{"{{focused}}", _focused},
{"{{windowFocusChanged}}", _windowFocusChanged},
{"{{focusWindow}}", _focusWindow},
};
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *line1Layout = new QHBoxLayout;
QHBoxLayout *line2Layout = new QHBoxLayout;
auto *line1Layout = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.window.entry.line1"),
line1Layout, widgetPlaceholders);
auto *line2Layout = new QHBoxLayout;
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.window.entry.line2"),
line2Layout, widgetPlaceholders);
placeWidgets(obs_module_text(
"AdvSceneSwitcher.condition.window.entry.line3"),
_focusLayout, widgetPlaceholders);
auto *mainLayout = new QVBoxLayout;
mainLayout->addLayout(line1Layout);
mainLayout->addLayout(line2Layout);
mainLayout->addLayout(_focusLayout);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
_timer.start(1000);
}
void MacroConditionWindowEdit::WindowChanged(const QString &text)
@ -192,6 +203,7 @@ void MacroConditionWindowEdit::FocusedChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_focus = state;
SetWidgetVisibility();
}
void MacroConditionWindowEdit::WindowFocusChanged(int state)
@ -202,6 +214,22 @@ void MacroConditionWindowEdit::WindowFocusChanged(int state)
std::lock_guard<std::mutex> lock(switcher->m);
_entryData->_windowFocusChanged = state;
SetWidgetVisibility();
}
void MacroConditionWindowEdit::UpdateFocusWindow()
{
_focusWindow->setText(QString::fromStdString(switcher->currentTitle));
}
void MacroConditionWindowEdit::SetWidgetVisibility()
{
if (!_entryData) {
return;
}
setLayoutVisible(_focusLayout,
_entryData->_focus || _entryData->_windowFocusChanged);
adjustSize();
}
void MacroConditionWindowEdit::UpdateEntryData()
@ -215,4 +243,5 @@ void MacroConditionWindowEdit::UpdateEntryData()
_maximized->setChecked(_entryData->_maximized);
_focused->setChecked(_entryData->_focus);
_windowFocusChanged->setChecked(_entryData->_windowFocusChanged);
SetWidgetVisibility();
}

View File

@ -18,9 +18,11 @@ public:
}
private:
bool CheckWindowTitleSwitchDirect(std::string &currentWindowTitle);
bool CheckWindowTitleSwitchRegex(std::string &currentWindowTitle,
std::vector<std::string> &windowList);
bool
CheckWindowTitleSwitchDirect(const std::string &currentWindowTitle);
bool
CheckWindowTitleSwitchRegex(const std::string &currentWindowTitle,
const std::vector<std::string> &windowList);
public:
std::string _window;
@ -56,6 +58,7 @@ private slots:
void MaximizedChanged(int state);
void FocusedChanged(int state);
void WindowFocusChanged(int state);
void UpdateFocusWindow();
signals:
void HeaderInfoChanged(const QString &);
@ -65,8 +68,13 @@ protected:
QCheckBox *_maximized;
QCheckBox *_focused;
QCheckBox *_windowFocusChanged;
QLabel *_focusWindow;
QHBoxLayout *_focusLayout;
QTimer _timer;
std::shared_ptr<MacroConditionWindow> _entryData;
private:
void SetWidgetVisibility();
bool _loading = true;
};

View File

@ -288,10 +288,38 @@ void GetProcessList(QStringList &list)
}
}
void GetForegroundProcessName(std::string &proc)
{
proc.resize(0);
@autoreleasepool {
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSArray *array = [ws runningApplications];
for (NSRunningApplication *app in array) {
if (!app.isActive) {
continue;
}
NSString *name = app.localizedName;
if (!name) {
break;
}
const char *str = name.UTF8String;
proc = std::string(str);
break;
}
}
}
void GetForegroundProcessName(QString &proc)
{
std::string temp;
GetForegroundProcessName(temp);
proc = QString::fromStdString(temp);
}
bool isInFocus(const QString &executable)
{
std::string current;
GetCurrentWindowTitle(current);
GetForegroundProcessName(current);
// True if executable switch equals current window
bool equals = (executable.toStdString() == current);

View File

@ -13,6 +13,7 @@ bool isMaximized(const std::string &title);
std::pair<int, int> getCursorPos();
int secondsSinceLastInput();
void GetProcessList(QStringList &processes);
void GetForegroundProcessName(std::string &name);
bool isInFocus(const QString &executable);
void PressKeys(const std::vector<HotkeyType> keys, int duration);
void PlatformInit();

View File

@ -148,6 +148,7 @@ struct SwitcherData {
std::vector<std::string> ignoreIdleWindows;
std::string lastTitle;
std::string currentTitle;
std::string currentForegroundProcess;
std::deque<ScreenRegionSwitch> screenRegionSwitches;
std::pair<int, int> lastCursorPos = {0, 0};

View File

@ -238,7 +238,7 @@ void GetProcessList(QStringList &processes)
CloseHandle(procSnapshot);
}
bool isInFocus(const QString &executable)
void GetForegroundProcessName(QString &proc)
{
// only checks if the current foreground window is from the same executable,
// may return true for any window from a program
@ -249,21 +249,36 @@ bool isInFocus(const QString &executable)
HANDLE process = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
if (process == NULL) {
return false;
return;
}
WCHAR executablePath[600];
GetModuleFileNameEx(process, 0, executablePath, 600);
CloseHandle(process);
QString file = QString::fromWCharArray(executablePath)
.split(QRegularExpression("(/|\\\\)"))
.back();
proc = QString::fromWCharArray(executablePath)
.split(QRegularExpression("(/|\\\\)"))
.back();
}
void GetForegroundProcessName(std::string &proc)
{
QString temp;
GetForegroundProcessName(temp);
proc = temp.toStdString();
}
bool isInFocus(const QString &executable)
{
// only checks if the current foreground window is from the same executable,
// may return true for any window from a program
QString foregroundProc;
GetForegroundProcessName(foregroundProc);
// True if executable switch equals current window
bool equals = (executable == file);
bool equals = (executable == foregroundProc);
// True if executable switch matches current window
bool matches = file.contains(QRegularExpression(executable));
bool matches = foregroundProc.contains(QRegularExpression(executable));
return (equals || matches);
}