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/CMakeLists.txt b/CMakeLists.txt
index 86deb4b7..ec478365 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-cmake_minimum_required (VERSION 3.4)
+cmake_minimum_required (VERSION 3.5)
project (barrier C CXX)
option (BARRIER_BUILD_GUI "Build the GUI" ON)
diff --git a/COMPILATION_GUIDE.md b/COMPILATION_GUIDE.md
new file mode 100644
index 00000000..53dced94
--- /dev/null
+++ b/COMPILATION_GUIDE.md
@@ -0,0 +1,265 @@
+# Barrier Linux Compilation Guide
+
+## Exact Steps Performed (What We Actually Did)
+
+This section documents the exact sequence of commands and fixes applied to successfully build Barrier on WSL2/Ubuntu.
+
+### 1. Fixed CMake Version Requirements
+Updated minimum CMake version from 3.4 to 3.5 in three files:
+
+**File 1:** `CMakeLists.txt` (line 18)
+```cmake
+# Changed from:
+cmake_minimum_required (VERSION 3.4)
+# To:
+cmake_minimum_required (VERSION 3.5)
+```
+
+**File 2:** `cmake/Version.cmake` (line 1)
+```cmake
+# Changed from:
+cmake_minimum_required (VERSION 3.4)
+# To:
+cmake_minimum_required (VERSION 3.5)
+```
+
+**File 3:** `src/gui/CMakeLists.txt` (line 1)
+```cmake
+# Changed from:
+cmake_minimum_required (VERSION 3.4)
+# To:
+cmake_minimum_required (VERSION 3.5)
+```
+
+### 2. Installed Dependencies
+```bash
+sudo apt install -y libcurl4-openssl-dev qtbase5-dev qttools5-dev-tools libssl-dev libx11-dev libxtst-dev libxinerama-dev libxss-dev libxi-dev libxrandr-dev libavahi-compat-libdnssd-dev
+```
+
+### 3. Fixed Missing Headers
+**File 1:** `src/lib/base/String.h`
+Added missing header after line 25:
+```cpp
+#include
+#include
+#include // ADDED THIS LINE
+```
+
+**File 2:** `src/lib/net/FingerprintData.h`
+Added missing header after line 22:
+```cpp
+#include
+#include
+#include // ADDED THIS LINE
+```
+
+### 4. Build
+```bash
+cd "/mnt/c/D Drive/CAPTAIN/CodeProjects/barrier"
+./clean_build.sh
+```
+
+### 5. Verify Build Output
+```bash
+ls -la "/mnt/c/D Drive/CAPTAIN/CodeProjects/barrier/build/bin/"
+```
+**Files created:**
+- `barriers` (12,709,936 bytes) - Server executable
+- `barrierc` (10,165,752 bytes) - Client executable
+- `integtests` (20,092,176 bytes) - Integration tests
+- `unittests` (15,227,792 bytes) - Unit tests
+
+### 6. Test Functionality
+```bash
+cd "/mnt/c/D Drive/CAPTAIN/CodeProjects/barrier/build/bin"
+./barrierc
+```
+**Result:** ✅ Successfully connected to server
+
+---
+
+## General Compilation Guide
+
+### Prerequisites
+
+#### System Requirements
+- Ubuntu/Debian-based Linux distribution
+- CMake 4.0+ (installed via snap)
+- GCC 13.3.0
+- Git
+
+#### Required Dependencies
+
+Install the following packages:
+
+```bash
+sudo apt update
+sudo apt install -y \
+ libcurl4-openssl-dev \
+ qtbase5-dev \
+ qttools5-dev-tools \
+ libssl-dev \
+ libx11-dev \
+ libxtst-dev \
+ libxinerama-dev \
+ libxss-dev \
+ libxi-dev \
+ libxrandr-dev \
+ libavahi-compat-libdnssd-dev
+```
+
+**Note on Package Names:**
+- Use `qtbase5-dev` instead of the deprecated `qt5-default`
+- Use `libavahi-compat-libdnssd-dev` instead of `avahi-compat-libdns_sd-dev`
+
+### Source Code Fixes Required
+
+The original Barrier source code requires several fixes to compile with modern toolchains:
+
+#### 1. CMake Minimum Version Updates
+
+**Files to modify:**
+- `CMakeLists.txt` (line 18)
+- `cmake/Version.cmake` (line 1)
+- `src/gui/CMakeLists.txt` (line 1)
+
+**Change:**
+```cmake
+# FROM:
+cmake_minimum_required (VERSION 3.4)
+
+# TO:
+cmake_minimum_required (VERSION 3.5)
+```
+
+**Reason:** CMake 4.0+ no longer supports compatibility with CMake < 3.5
+
+#### 2. Missing C++ Headers
+
+**File:** `src/lib/base/String.h`
+
+**Add after existing includes:**
+```cpp
+#include
+#include
+#include // ADD THIS LINE
+```
+
+**File:** `src/lib/net/FingerprintData.h`
+
+**Add after existing includes:**
+```cpp
+#include
+#include
+#include // ADD THIS LINE
+```
+
+**Reason:** Modern C++ compilers require explicit inclusion of `` for `std::uint8_t` types
+
+### Compilation Process
+
+#### 1. Clone and Navigate
+```bash
+git clone
+cd barrier
+```
+
+#### 2. Initialize Submodules
+```bash
+git submodule update --init --recursive
+```
+
+#### 3. Apply Source Code Fixes
+Apply all the fixes mentioned in the "Source Code Fixes Required" section above.
+
+#### 4. Build
+```bash
+./clean_build.sh
+```
+
+**Alternative manual build:**
+```bash
+mkdir build && cd build
+cmake -DCMAKE_BUILD_TYPE=Debug ..
+make
+```
+
+### Build Output
+
+#### Successful Build Results
+- **Location:** `build/bin/`
+- **Core executables:**
+ - `barriers` (~12.7MB) - Server component
+ - `barrierc` (~10.2MB) - Client component
+ - `barrier` - GUI application (if Qt build completes)
+ - `integtests` - Integration tests
+ - `unittests` - Unit tests
+
+#### Expected Warnings
+- OpenSSL deprecation warnings (non-critical)
+- CMake compatibility warnings (non-critical)
+- Clock skew warnings in WSL (non-critical)
+
+### Usage
+
+#### Server Mode
+```bash
+cd build/bin
+./barriers
+```
+
+#### Client Mode
+```bash
+cd build/bin
+./barrierc
+```
+
+#### GUI Mode (if available)
+```bash
+cd build/bin
+./barrier
+```
+
+### Troubleshooting
+
+#### Common Issues
+
+1. **Missing Qt5**: Install `qtbase5-dev` instead of `qt5-default`
+2. **Missing Avahi**: Use `libavahi-compat-libdnssd-dev` package name
+3. **uint8_t errors**: Add `#include ` to header files
+4. **CMake version errors**: Update minimum version requirements to 3.5
+5. **CURL not found**: Install `libcurl4-openssl-dev`
+
+#### Build Environment Notes
+- **WSL Users**: No GUI display available by default
+- **X11 Required**: For actual mouse/keyboard sharing functionality
+- **Network**: Ensure firewall allows Barrier traffic (default port 24800)
+
+### Platform-Specific Notes
+
+#### Windows Build (Alternative)
+If building on Windows instead of Linux:
+- Requires Visual Studio 2019/2022
+- Requires Qt 5.11.1 at `C:\Qt\5.11.1\msvc2017_64`
+- Requires Bonjour SDK at `C:\Program Files\Bonjour SDK`
+- Use `clean_build.bat` instead of `clean_build.sh`
+
+#### Linux Desktop Environment
+For full functionality, Barrier requires:
+- X11 or Wayland display server
+- Desktop environment (GNOME, KDE, etc.)
+- Proper graphics drivers
+
+### Version Information
+- **Barrier Version:** 2.4.0
+- **Build System:** CMake + Make
+- **Compiler:** GCC 13.3.0
+- **Target Platform:** Linux x86_64
+
+### Summary of Changes Made
+1. Updated CMake minimum version requirements (3 files)
+2. Added missing `#include ` headers (2 files)
+3. Installed correct Linux development packages
+4. Used appropriate package names for Ubuntu 24.04+
+
+This guide should enable successful compilation of Barrier on modern Linux systems.
\ No newline at end of file
diff --git a/GRAVE_MODIFIER_SETUP.md b/GRAVE_MODIFIER_SETUP.md
new file mode 100644
index 00000000..6712fbc4
--- /dev/null
+++ b/GRAVE_MODIFIER_SETUP.md
@@ -0,0 +1,48 @@
+# Grave Modifier Setup for Barrier
+
+## Overview
+This implementation adds support for using the grave key (`) as a hotkey modifier in Barrier, similar to Control, Alt, and Shift.
+
+## Required Setup
+
+### 1. Code Changes
+The following code changes have been implemented:
+
+- **XWindowsUtil.cpp**: Added `case XK_grave: return kKeyModifierBitGrave;` to `getModifierBitForKeySym()`
+- **XWindowsKeyState.cpp**: Added fallback mapping for grave modifier to Mod4 in `updateKeysymMapXKB()`
+- **key_types.cpp**: Already included grave modifier in `kModifierNameMap`
+
+### 2. X11 Modifier Mapping (REQUIRED)
+The grave key must be mapped to an X11 modifier for hotkeys to work. Run this command:
+
+```bash
+xmodmap -e "add mod4 = grave"
+```
+
+### 3. Make it Permanent
+To make the X11 mapping persistent across reboots, add this line to `~/.xsessionrc`:
+
+```bash
+echo 'xmodmap -e "add mod4 = grave"' >> ~/.xsessionrc
+```
+
+## Usage
+Once setup is complete, you can use grave modifier hotkeys in your Barrier configuration:
+
+```
+keystroke(Grave+1) = switchToScreen(screen1,960,540)
+keystroke(Grave+2) = switchToScreen(screen2,960,540)
+```
+
+## Verification
+To verify the setup:
+
+1. Check modifier mapping: `xmodmap -pm`
+ - Should show `grave (0x31)` in the mod4 line
+2. Test hotkeys: Press and hold grave, then press a number key
+3. Check Barrier logs for successful hotkey registration
+
+## Troubleshooting
+- If hotkeys don't work, verify grave is mapped to mod4: `xmodmap -pm`
+- If mapping is lost after reboot, ensure `~/.xsessionrc` contains the xmodmap command
+- Restart Barrier server after making X11 mapping changes
\ No newline at end of file
diff --git a/cmake/Version.cmake b/cmake/Version.cmake
index 73524bf1..dfb83bbb 100644
--- a/cmake/Version.cmake
+++ b/cmake/Version.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.4)
+cmake_minimum_required (VERSION 3.5)
set (BARRIER_VERSION_MAJOR 2)
set (BARRIER_VERSION_MINOR 4)
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 570e8424..fbad2ec7 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.4)
+cmake_minimum_required (VERSION 3.5)
find_package (Qt5 REQUIRED COMPONENTS Core Widgets Network)
set (CMAKE_AUTOMOC ON)
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/gui/src/KeySequence.cpp b/src/gui/src/KeySequence.cpp
index ddf7d332..3ef93926 100644
--- a/src/gui/src/KeySequence.cpp
+++ b/src/gui/src/KeySequence.cpp
@@ -139,6 +139,19 @@ bool KeySequence::appendKey(int key, int modifiers)
}
break;
+ case Qt::Key_QuoteLeft:
+ {
+ printf("GRAVE DEBUG: Qt::Key_QuoteLeft detected in GUI\n");
+ int mod = GraveModifier & (~m_Modifiers);
+ if (mod)
+ {
+ printf("GRAVE DEBUG: Adding GraveModifier to sequence: %08x\n", mod);
+ m_Sequence.append(mod);
+ m_Modifiers |= mod;
+ }
+ }
+ break;
+
default:
// see if we can handle this key, if not, don't accept it
if (keyToString(key).isEmpty())
@@ -210,6 +223,9 @@ QString KeySequence::keyToString(int key)
if (key & Qt::MetaModifier)
return "Meta";
+ if (key & GraveModifier)
+ return "Grave";
+
// treat key pad like normal keys (FIXME: we should have another lookup table for keypad keys instead)
key &= ~Qt::KeypadModifier;
diff --git a/src/gui/src/KeySequence.h b/src/gui/src/KeySequence.h
index 03310918..56c3f0bf 100644
--- a/src/gui/src/KeySequence.h
+++ b/src/gui/src/KeySequence.h
@@ -25,6 +25,9 @@
class QSettings;
+// Custom modifier for grave key (not supported by Qt)
+static const int GraveModifier = 0x20000000;
+
class KeySequence
{
public:
diff --git a/src/lib/barrier/key_types.cpp b/src/lib/barrier/key_types.cpp
index 33ee3ebc..06e9d5e9 100644
--- a/src/lib/barrier/key_types.cpp
+++ b/src/lib/barrier/key_types.cpp
@@ -209,6 +209,7 @@ const KeyModifierNameMapEntry kModifierNameMap[] = {
{ "AltGr", KeyModifierAltGr },
// { "CapsLock", KeyModifierCapsLock },
{ "Control", KeyModifierControl },
+ { "Grave", KeyModifierGrave },
{ "Meta", KeyModifierMeta },
// { "NumLock", KeyModifierNumLock },
// { "ScrollLock", KeyModifierScrollLock },
diff --git a/src/lib/barrier/key_types.h b/src/lib/barrier/key_types.h
index 863dc174..39635a6b 100644
--- a/src/lib/barrier/key_types.h
+++ b/src/lib/barrier/key_types.h
@@ -58,6 +58,7 @@ static const KeyModifierMask KeyModifierMeta = 0x0008;
static const KeyModifierMask KeyModifierSuper = 0x0010;
static const KeyModifierMask KeyModifierAltGr = 0x0020;
static const KeyModifierMask KeyModifierLevel5Lock = 0x0040;
+static const KeyModifierMask KeyModifierGrave = 0x0080;
static const KeyModifierMask KeyModifierCapsLock = 0x1000;
static const KeyModifierMask KeyModifierNumLock = 0x2000;
static const KeyModifierMask KeyModifierScrollLock = 0x4000;
@@ -73,6 +74,7 @@ static const UInt32 kKeyModifierBitMeta = 3;
static const UInt32 kKeyModifierBitSuper = 4;
static const UInt32 kKeyModifierBitAltGr = 5;
static const UInt32 kKeyModifierBitLevel5Lock = 6;
+static const UInt32 kKeyModifierBitGrave = 7;
static const UInt32 kKeyModifierBitCapsLock = 12;
static const UInt32 kKeyModifierBitNumLock = 13;
static const UInt32 kKeyModifierBitScrollLock = 14;
@@ -88,7 +90,8 @@ static const KeyModifierID kKeyModifierIDAlt = 3;
static const KeyModifierID kKeyModifierIDMeta = 4;
static const KeyModifierID kKeyModifierIDSuper = 5;
static const KeyModifierID kKeyModifierIDAltGr = 6;
-static const KeyModifierID kKeyModifierIDLast = 7;
+static const KeyModifierID kKeyModifierIDGrave = 7;
+static const KeyModifierID kKeyModifierIDLast = 8;
//@}
//! @name Key identifiers
diff --git a/src/lib/base/String.h b/src/lib/base/String.h
index 9c5a53ba..faf35969 100644
--- a/src/lib/base/String.h
+++ b/src/lib/base/String.h
@@ -23,6 +23,7 @@
#include
#include
+#include
// use standard C++ string class for our string class
typedef std::string String;
diff --git a/src/lib/net/FingerprintData.h b/src/lib/net/FingerprintData.h
index 938a6953..8ab16314 100644
--- a/src/lib/net/FingerprintData.h
+++ b/src/lib/net/FingerprintData.h
@@ -20,6 +20,7 @@
#include
#include
+#include
namespace barrier {
diff --git a/src/lib/platform/XWindowsKeyState.cpp b/src/lib/platform/XWindowsKeyState.cpp
index 088adc6d..714ee32f 100644
--- a/src/lib/platform/XWindowsKeyState.cpp
+++ b/src/lib/platform/XWindowsKeyState.cpp
@@ -317,6 +317,7 @@ XWindowsKeyState::updateKeysymMap(barrier::KeyMap& keyMap)
m_modifierToX[KeyModifierShift] = ShiftMask;
m_modifierToX[KeyModifierCapsLock] = LockMask;
m_modifierToX[KeyModifierControl] = ControlMask;
+ m_modifierToX[KeyModifierGrave] = Mod4Mask;
// prepare map from KeyID to KeyCode
m_keyCodeFromKey.clear();
@@ -779,6 +780,16 @@ XWindowsKeyState::updateKeysymMapXKB(barrier::KeyMap& keyMap)
}
}
+ // Add fallback for grave modifier if not found during XKB discovery
+ if (m_modifierToX.find(KeyModifierGrave) == m_modifierToX.end()) {
+ LOG((CLOG_DEBUG1 "XKB: Adding fallback mapping for grave modifier to Mod4"));
+ m_modifierToX[KeyModifierGrave] = Mod4Mask;
+ // Also update the reverse mapping for consistency
+ if (m_modifierFromX.size() > 3) { // Mod4 is bit 3
+ m_modifierFromX[3] |= KeyModifierGrave;
+ }
+ }
+
// change all modifier masks to barrier masks from X masks
keyMap.foreachKey(&XWindowsKeyState::remapKeyModifiers, this);
diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp
index 7f8ce655..47b54702 100644
--- a/src/lib/platform/XWindowsScreen.cpp
+++ b/src/lib/platform/XWindowsScreen.cpp
@@ -528,7 +528,7 @@ XWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
{
// only allow certain modifiers
if ((mask & ~(KeyModifierShift | KeyModifierControl |
- KeyModifierAlt | KeyModifierSuper)) != 0) {
+ KeyModifierAlt | KeyModifierSuper | KeyModifierGrave)) != 0) {
LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
return 0;
}
diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp
index 7d4bb638..870b40a4 100644
--- a/src/lib/platform/XWindowsUtil.cpp
+++ b/src/lib/platform/XWindowsUtil.cpp
@@ -1646,6 +1646,9 @@ XWindowsUtil::getModifierBitForKeySym(KeySym keysym)
case XK_Scroll_Lock:
return kKeyModifierBitScrollLock;
+ case XK_grave:
+ return kKeyModifierBitGrave;
+
default:
return kKeyModifierBitNone;
}
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];
};