Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions hyprtester/plugin/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
#include <src/Compositor.hpp>
#include <src/desktop/state/FocusState.hpp>
#undef private

#include <hyprutils/utils/ScopeGuard.hpp>
Expand Down Expand Up @@ -45,7 +46,7 @@ static SDispatchResult test(std::string in) {

// Trigger a snap move event for the active window
static SDispatchResult snapMove(std::string in) {
const auto PLASTWINDOW = g_pCompositor->m_lastWindow.lock();
const auto PLASTWINDOW = Desktop::focusState()->window();
if (!PLASTWINDOW->m_isFloating)
return {.success = false, .error = "Window must be floating"};

Expand Down Expand Up @@ -259,13 +260,15 @@ static SDispatchResult addRule(std::string in) {
}

static SDispatchResult checkRule(std::string in) {
if (!g_pCompositor->m_lastWindow)
const auto PLASTWINDOW = Desktop::focusState()->window();

if (!PLASTWINDOW)
return {.success = false, .error = "No window"};

if (!g_pCompositor->m_lastWindow->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
return {.success = false, .error = "No rule"};

if (g_pCompositor->m_lastWindow->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
return {.success = false, .error = "Effect isn't \"effect\""};

return {};
Expand Down
18 changes: 14 additions & 4 deletions hyprtester/src/tests/main/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ static bool test() {

NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW);

OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));

Tests::spawnKitty("kitty_A");

Expand All @@ -73,7 +73,16 @@ static bool test() {
EXPECT_CONTAINS(str, "kitty_A");
}

OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 1"));
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));

{
// should be ignored as per focus_under_fullscreen 0
auto str = getFromSocket("/activewindow");
EXPECT_CONTAINS(str, "fullscreen: 2");
EXPECT_CONTAINS(str, "kitty_A");
}

OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));

Tests::spawnKitty("kitty_C");

Expand All @@ -83,7 +92,7 @@ static bool test() {
EXPECT_CONTAINS(str, "kitty_C");
}

OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 2"));
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));

Tests::spawnKitty("kitty_D");

Expand All @@ -93,7 +102,7 @@ static bool test() {
EXPECT_CONTAINS(str, "kitty_D");
}

OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));

Tests::killAllWindows();

Expand Down Expand Up @@ -138,6 +147,7 @@ static bool test() {
Tests::spawnKitty("kitty_A");
Tests::spawnKitty("kitty_B");

OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));

{
Expand Down
197 changes: 184 additions & 13 deletions hyprtester/src/tests/main/window.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <cmath>
#include <thread>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <thread>
#include <hyprutils/os/Process.hpp>
#include <hyprutils/memory/WeakPtr.hpp>

Expand All @@ -11,9 +14,9 @@

static int ret = 0;

static bool spawnKitty(const std::string& class_) {
static bool spawnKitty(const std::string& class_, const std::vector<std::string>& args = {}) {
NLog::log("{}Spawning {}", Colors::YELLOW, class_);
if (!Tests::spawnKitty(class_)) {
if (!Tests::spawnKitty(class_, args)) {
NLog::log("{}Error: {} did not spawn", Colors::RED, class_);
return false;
}
Expand Down Expand Up @@ -193,6 +196,153 @@ static void testGroupRules() {
Tests::killAllWindows();
}

static bool isActiveWindow(const std::string& class_, char fullscreen, bool log = true) {
std::string activeWin = getFromSocket("/activewindow");
auto winClass = getWindowAttribute(activeWin, "class:");
auto winFullscreen = getWindowAttribute(activeWin, "fullscreen:").back();
if (winClass.substr(strlen("class: ")) == class_ && winFullscreen == fullscreen)
return true;
else {
if (log)
NLog::log("{}Wrong active window: expected class {} fullscreen '{}', found class {}, fullscreen '{}'", Colors::RED, class_, fullscreen, winClass, winFullscreen);
return false;
}
}

static bool waitForActiveWindow(const std::string& class_, char fullscreen, int maxTries = 50) {
int cnt = 0;
while (!isActiveWindow(class_, fullscreen, false)) {
++cnt;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (cnt > maxTries) {
return isActiveWindow(class_, fullscreen, true);
}
}
return true;
}

/// Tests behavior of a window being focused when on that window's workspace
/// another fullscreen window exists.
static bool testWindowFocusOnFullscreenConflict() {
if (!spawnKitty("kitty_A"))
return false;
if (!spawnKitty("kitty_B"))
return false;

OK(getFromSocket("/keyword misc:focus_on_activate true"));

auto spawnKittyActivating = [] -> std::string {
// `XXXXXX` is what `mkstemp` expects to find in the string
std::string tmpFilename = (std::filesystem::temp_directory_path() / "XXXXXX").string();
int fd = mkstemp(tmpFilename.data());
if (fd < 0) {
NLog::log("{}Error: could not create tmp file: errno {}", Colors::RED, errno);
return "";
}
(void)close(fd);
bool ok = spawnKitty("kitty_activating",
{"-o", "allow_remote_control=yes", "--", "/bin/sh", "-c", "while [ -f \"" + tmpFilename + "\" ]; do :; done; kitten @ focus-window; sleep infinity"});
if (!ok) {
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
return "";
}
return tmpFilename;
};

// Unfullscreen on conflict
{
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));

OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(isActiveWindow("kitty_A", '2'), true);

// Dispatch-focus the same window
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
EXPECT(isActiveWindow("kitty_A", '2'), true);

// Dispatch-focus a different window
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
EXPECT(isActiveWindow("kitty_B", '0'), true);

// Make a window that will request focus
const std::string removeToActivate = spawnKittyActivating();
if (removeToActivate.empty())
return false;
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(isActiveWindow("kitty_A", '2'), true);
std::filesystem::remove(removeToActivate);
EXPECT(waitForActiveWindow("kitty_activating", '0'), true);
OK(getFromSocket("/dispatch forcekillactive"));
Tests::waitUntilWindowsN(2);
}

// Take over on conflict
{
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));

OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(isActiveWindow("kitty_A", '2'), true);

// Dispatch-focus the same window
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
EXPECT(isActiveWindow("kitty_A", '2'), true);

// Dispatch-focus a different window
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
EXPECT(isActiveWindow("kitty_B", '2'), true);
OK(getFromSocket("/dispatch fullscreenstate 0 0"));

// Make a window that will request focus
const std::string removeToActivate = spawnKittyActivating();
if (removeToActivate.empty())
return false;
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(isActiveWindow("kitty_A", '2'), true);
std::filesystem::remove(removeToActivate);
EXPECT(waitForActiveWindow("kitty_activating", '2'), true);
OK(getFromSocket("/dispatch forcekillactive"));
Tests::waitUntilWindowsN(2);
}

// Keep the old focus on conflict
{
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));

OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(isActiveWindow("kitty_A", '2'), true);

// Dispatch-focus the same window
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
EXPECT(isActiveWindow("kitty_A", '2'), true);

// Make a window that will request focus - the setting is treated normally
const std::string removeToActivate = spawnKittyActivating();
if (removeToActivate.empty())
return false;
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(isActiveWindow("kitty_A", '2'), true);
std::filesystem::remove(removeToActivate);
EXPECT(waitForActiveWindow("kitty_A", '2'), true);
}

NLog::log("{}Reloading config", Colors::YELLOW);
OK(getFromSocket("/reload"));

NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows();

NLog::log("{}Expecting 0 windows", Colors::YELLOW);
EXPECT(Tests::windowCount(), 0);

return true;
}

static bool test() {
NLog::log("{}Testing windows", Colors::GREEN);

Expand Down Expand Up @@ -256,16 +406,7 @@ static bool test() {
getFromSocket("/dispatch exec xeyes");

NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW);
int counter = 0;
while (Tests::windowCount() != 3) {
counter++;
std::this_thread::sleep_for(std::chrono::milliseconds(100));

if (counter > 50) {
EXPECT(Tests::windowCount(), 3);
return !ret;
}
}
Tests::waitUntilWindowsN(3);

NLog::log("{}Expecting 3 windows", Colors::YELLOW);
EXPECT(Tests::windowCount(), 3);
Expand All @@ -292,6 +433,36 @@ static bool test() {

getFromSocket("/dispatch workspace 1");

if (!testWindowFocusOnFullscreenConflict()) {
ret = 1;
return false;
}

NLog::log("{}Testing spawning a floating window over a fullscreen window", Colors::YELLOW);
{
if (!spawnKitty("kitty_A"))
return false;
OK(getFromSocket("/dispatch fullscreen 0 set"));
EXPECT(Tests::windowCount(), 1);

OK(getFromSocket("/dispatch exec [float] kitty"));
Tests::waitUntilWindowsN(2);

OK(getFromSocket("/dispatch focuswindow class:^kitty$"));
const auto focused1 = getFromSocket("/activewindow");
EXPECT_CONTAINS(focused1, "class: kitty\n");

OK(getFromSocket("/dispatch killwindow activewindow"));
Tests::waitUntilWindowsN(1);

// The old window should be focused again
const auto focused2 = getFromSocket("/activewindow");
EXPECT_CONTAINS(focused2, "class: kitty_A\n");

NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows();
}

NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW);
{
// Enable the config for testing, test max/minsize for tiled windows and centering
Expand Down
Loading
Loading