diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 00000000..50941707
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,12 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(rg:*)",
+ "Bash(g++:*)",
+ "Bash(cmake:*)",
+ "Bash(sudo apt:*)",
+ "Bash(sudo apt install:*)"
+ ],
+ "deny": []
+ }
+}
\ No newline at end of file
diff --git a/src/gui/src/Action.cpp b/src/gui/src/Action.cpp
index f34d1e8f..cf4f5ddf 100644
--- a/src/gui/src/Action.cpp
+++ b/src/gui/src/Action.cpp
@@ -40,7 +40,10 @@ Action::Action() :
m_SwitchDirection(switchLeft),
m_LockCursorMode(lockCursorToggle),
m_ActiveOnRelease(false),
- m_HasScreens(false)
+ m_HasScreens(false),
+ m_hasCustomPosition(false),
+ m_customX(0),
+ m_customY(0)
{
}
@@ -87,6 +90,12 @@ QString Action::text() const
case switchToScreen:
text += "(";
text += switchScreenName();
+ if (hasCustomPosition()) {
+ text += ",";
+ text += QString::number(customX());
+ text += ",";
+ text += QString::number(customY());
+ }
text += ")";
break;
@@ -133,6 +142,9 @@ void Action::loadSettings(QSettings& settings)
setLockCursorMode(settings.value("lockCursorToScreen", lockCursorToggle).toInt());
setActiveOnRelease(settings.value("activeOnRelease", false).toBool());
setHaveScreens(settings.value("hasScreens", false).toBool());
+ setHasCustomPosition(settings.value("hasCustomPosition", false).toBool());
+ setCustomX(settings.value("customX", 0).toInt());
+ setCustomY(settings.value("customY", 0).toInt());
}
void Action::saveSettings(QSettings& settings) const
@@ -153,6 +165,9 @@ void Action::saveSettings(QSettings& settings) const
settings.setValue("lockCursorToScreen", lockCursorMode());
settings.setValue("activeOnRelease", activeOnRelease());
settings.setValue("hasScreens", haveScreens());
+ settings.setValue("hasCustomPosition", hasCustomPosition());
+ settings.setValue("customX", customX());
+ settings.setValue("customY", customY());
}
QTextStream& operator<<(QTextStream& outStream, const Action& action)
diff --git a/src/gui/src/Action.h b/src/gui/src/Action.h
index c260badc..f5ff55ac 100644
--- a/src/gui/src/Action.h
+++ b/src/gui/src/Action.h
@@ -72,6 +72,15 @@ class Action
bool haveScreens() const { return m_HasScreens; }
void setHaveScreens(bool b) { m_HasScreens = b; }
+ bool hasCustomPosition() const { return m_hasCustomPosition; }
+ void setHasCustomPosition(bool b) { m_hasCustomPosition = b; }
+
+ int customX() const { return m_customX; }
+ void setCustomX(int x) { m_customX = x; }
+
+ int customY() const { return m_customY; }
+ void setCustomY(int y) { m_customY = y; }
+
private:
KeySequence m_KeySequence;
int m_Type;
@@ -81,6 +90,9 @@ class Action
int m_LockCursorMode;
bool m_ActiveOnRelease;
bool m_HasScreens;
+ bool m_hasCustomPosition;
+ int m_customX;
+ int m_customY;
static const char* m_ActionTypeNames[];
static const char* m_SwitchDirectionNames[];
diff --git a/src/gui/src/ActionDialog.cpp b/src/gui/src/ActionDialog.cpp
index 89a037e3..8032b85b 100644
--- a/src/gui/src/ActionDialog.cpp
+++ b/src/gui/src/ActionDialog.cpp
@@ -73,6 +73,11 @@ ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey
idx++;
}
}
+
+ // Initialize coordinate controls
+ m_pCheckBoxCustomPosition->setChecked(m_Action.hasCustomPosition());
+ m_pSpinBoxX->setValue(m_Action.customX());
+ m_pSpinBoxY->setValue(m_Action.customY());
}
void ActionDialog::accept()
@@ -93,6 +98,23 @@ void ActionDialog::accept()
m_Action.setSwitchDirection(m_pComboSwitchInDirection->currentIndex());
m_Action.setLockCursorMode(m_pComboLockCursorToScreen->currentIndex());
m_Action.setActiveOnRelease(m_pRadioHotkeyReleased->isChecked());
+
+ // Save coordinate settings only for switchToScreen action
+ if (m_pButtonGroupType->checkedId() == Action::switchToScreen) {
+ m_Action.setHasCustomPosition(m_pCheckBoxCustomPosition->isChecked());
+ if (m_pCheckBoxCustomPosition->isChecked()) {
+ m_Action.setCustomX(m_pSpinBoxX->value());
+ m_Action.setCustomY(m_pSpinBoxY->value());
+ } else {
+ m_Action.setCustomX(0);
+ m_Action.setCustomY(0);
+ }
+ } else {
+ // Clear custom position for other action types
+ m_Action.setHasCustomPosition(false);
+ m_Action.setCustomX(0);
+ m_Action.setCustomY(0);
+ }
QDialog::accept();
}
diff --git a/src/gui/src/ActionDialogBase.ui b/src/gui/src/ActionDialogBase.ui
index 9c6ad0a0..4036bd3a 100644
--- a/src/gui/src/ActionDialogBase.ui
+++ b/src/gui/src/ActionDialogBase.ui
@@ -142,6 +142,96 @@
+ -
+
+
-
+
+
+ Set specific position
+
+
+ false
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+ X:
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+ 0
+
+
+ 9999
+
+
+
+ -
+
+
+ Y:
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+ 0
+
+
+ 9999
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
-
-
@@ -588,5 +678,85 @@
+
+ m_pRadioSwitchToScreen
+ toggled(bool)
+ m_pCheckBoxCustomPosition
+ setEnabled(bool)
+
+
+ 148
+ 291
+
+
+ 148
+ 310
+
+
+
+
+ m_pCheckBoxCustomPosition
+ toggled(bool)
+ m_pLabelX
+ setEnabled(bool)
+
+
+ 148
+ 310
+
+
+ 50
+ 330
+
+
+
+
+ m_pCheckBoxCustomPosition
+ toggled(bool)
+ m_pSpinBoxX
+ setEnabled(bool)
+
+
+ 148
+ 310
+
+
+ 100
+ 330
+
+
+
+
+ m_pCheckBoxCustomPosition
+ toggled(bool)
+ m_pLabelY
+ setEnabled(bool)
+
+
+ 148
+ 310
+
+
+ 150
+ 330
+
+
+
+
+ m_pCheckBoxCustomPosition
+ toggled(bool)
+ m_pSpinBoxY
+ setEnabled(bool)
+
+
+ 148
+ 310
+
+
+ 200
+ 330
+
+
+
diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp
index bcdb88cd..52913028 100644
--- a/src/lib/server/Config.cpp
+++ b/src/lib/server/Config.cpp
@@ -1141,8 +1141,8 @@ void Config::parseAction(ConfigReadContext& s, const std::string& name,
*/
else if (name == "switchToScreen") {
- if (args.size() != 1) {
- throw XConfigRead(s, "syntax for action: switchToScreen(name)");
+ if (args.size() != 1 && args.size() != 3) {
+ throw XConfigRead(s, "syntax for action: switchToScreen(name[,x,y])");
}
std::string screen = args[0];
@@ -1153,7 +1153,19 @@ void Config::parseAction(ConfigReadContext& s, const std::string& name,
throw XConfigRead(s, "unknown screen name in switchToScreen");
}
- action = new InputFilter::SwitchToScreenAction(m_events, screen);
+ if (args.size() == 3) {
+ // Parse coordinates
+ int x, y;
+ try {
+ x = std::stoi(args[1]);
+ y = std::stoi(args[2]);
+ } catch (const std::exception&) {
+ throw XConfigRead(s, "invalid coordinates in switchToScreen");
+ }
+ action = new InputFilter::SwitchToScreenAction(m_events, screen, x, y);
+ } else {
+ action = new InputFilter::SwitchToScreenAction(m_events, screen);
+ }
}
else if (name == "toggleScreen") {
diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp
index a0dce17c..c2f7ad9f 100644
--- a/src/lib/server/InputFilter.cpp
+++ b/src/lib/server/InputFilter.cpp
@@ -322,6 +322,20 @@ InputFilter::LockCursorToScreenAction::perform(const Event& event)
InputFilter::SwitchToScreenAction::SwitchToScreenAction(IEventQueue* events,
const std::string& screen) :
m_screen(screen),
+ m_hasCustomPosition(false),
+ m_x(0),
+ m_y(0),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::SwitchToScreenAction::SwitchToScreenAction(IEventQueue* events,
+ const std::string& screen, int x, int y) :
+ m_screen(screen),
+ m_hasCustomPosition(true),
+ m_x(x),
+ m_y(y),
m_events(events)
{
// do nothing
@@ -332,15 +346,38 @@ std::string InputFilter::SwitchToScreenAction::getScreen() const
return m_screen;
}
+bool InputFilter::SwitchToScreenAction::hasCustomPosition() const
+{
+ return m_hasCustomPosition;
+}
+
+int InputFilter::SwitchToScreenAction::getX() const
+{
+ return m_x;
+}
+
+int InputFilter::SwitchToScreenAction::getY() const
+{
+ return m_y;
+}
+
InputFilter::Action*
InputFilter::SwitchToScreenAction::clone() const
{
- return new SwitchToScreenAction(*this);
+ if (m_hasCustomPosition) {
+ return new SwitchToScreenAction(m_events, m_screen, m_x, m_y);
+ } else {
+ return new SwitchToScreenAction(m_events, m_screen);
+ }
}
std::string InputFilter::SwitchToScreenAction::format() const
{
- return barrier::string::sprintf("switchToScreen(%s)", m_screen.c_str());
+ if (m_hasCustomPosition) {
+ return barrier::string::sprintf("switchToScreen(%s,%d,%d)", m_screen.c_str(), m_x, m_y);
+ } else {
+ return barrier::string::sprintf("switchToScreen(%s)", m_screen.c_str());
+ }
}
void
@@ -356,8 +393,12 @@ InputFilter::SwitchToScreenAction::perform(const Event& event)
}
// send event
- Server::SwitchToScreenInfo* info =
- Server::SwitchToScreenInfo::alloc(screen);
+ Server::SwitchToScreenInfo* info;
+ if (m_hasCustomPosition) {
+ info = Server::SwitchToScreenInfo::alloc(screen, m_x, m_y);
+ } else {
+ info = Server::SwitchToScreenInfo::alloc(screen);
+ }
m_events->addEvent(Event(m_events->forServer().switchToScreen(),
event.getTarget(), info,
Event::kDeliverImmediately));
diff --git a/src/lib/server/InputFilter.h b/src/lib/server/InputFilter.h
index 5e6ef9cf..61f0bc55 100644
--- a/src/lib/server/InputFilter.h
+++ b/src/lib/server/InputFilter.h
@@ -153,8 +153,12 @@ public:
class SwitchToScreenAction : public Action {
public:
SwitchToScreenAction(IEventQueue* events, const std::string& screen);
+ SwitchToScreenAction(IEventQueue* events, const std::string& screen, int x, int y);
std::string getScreen() const;
+ bool hasCustomPosition() const;
+ int getX() const;
+ int getY() const;
// Action overrides
virtual Action* clone() const;
@@ -163,6 +167,9 @@ public:
private:
std::string m_screen;
+ bool m_hasCustomPosition;
+ int m_x;
+ int m_y;
IEventQueue* m_events;
};
diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp
index a169db16..acb964f2 100644
--- a/src/lib/server/Server.cpp
+++ b/src/lib/server/Server.cpp
@@ -1404,7 +1404,13 @@ Server::handleSwitchToScreenEvent(const Event& event, void*)
LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen));
}
else {
- jumpToScreen(index->second);
+ if (info->m_hasCustomPosition) {
+ // Switch to specific coordinates
+ switchScreen(index->second, info->m_x, info->m_y, false);
+ } else {
+ // Use default behavior - jump to last cursor position
+ jumpToScreen(index->second);
+ }
}
}
@@ -2329,6 +2335,22 @@ Server::SwitchToScreenInfo::alloc(const std::string& screen)
SwitchToScreenInfo* info =
(SwitchToScreenInfo*)malloc(sizeof(SwitchToScreenInfo) +
screen.size());
+ info->m_hasCustomPosition = false;
+ info->m_x = 0;
+ info->m_y = 0;
+ strcpy(info->m_screen, screen.c_str());
+ return info;
+}
+
+Server::SwitchToScreenInfo*
+Server::SwitchToScreenInfo::alloc(const std::string& screen, SInt32 x, SInt32 y)
+{
+ SwitchToScreenInfo* info =
+ (SwitchToScreenInfo*)malloc(sizeof(SwitchToScreenInfo) +
+ screen.size());
+ info->m_hasCustomPosition = true;
+ info->m_x = x;
+ info->m_y = y;
strcpy(info->m_screen, screen.c_str());
return info;
}
diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h
index ae8b2bd5..40f8e7b8 100644
--- a/src/lib/server/Server.h
+++ b/src/lib/server/Server.h
@@ -63,8 +63,12 @@ public:
class SwitchToScreenInfo {
public:
static SwitchToScreenInfo* alloc(const std::string& screen);
+ static SwitchToScreenInfo* alloc(const std::string& screen, SInt32 x, SInt32 y);
public:
+ bool m_hasCustomPosition;
+ SInt32 m_x;
+ SInt32 m_y;
// this is a C-string; this type is a variable size structure
char m_screen[1];
};