15 October 2025

Creating Reliable Keyboard Shortcuts for Copy/Paste and Middle-Click


# Creating Reliable Keyboard Shortcuts for Copy/Paste and Middle-Click

## Problem: The Need for Alternative Shortcuts

I found that relying solely on the mouse for basic text operations was limiting:

1.  My **mouse scroll wheel button (Middle-click)**, often used for quickly pasting selected text, was becoming **unreliable**.
2.  I frequently **dislike moving my right hand** from the arrow/navigation keys and Enter area to reach $\text{Ctrl}$+$\text{C}$ or $\text{Ctrl}$+$\text{V}$.

This setup requires a more reliable and ergonomic alternative that keeps my right hand near the arrow keys and the left one on the mouse.

### Context

This solution was developed and tested on **Debian 10 (Buster)** using the **LXDE desktop environment** with the **Openbox Window Manager**.

-----

## Solution: Mapping to the Super Key and Arrow Keys

The solution is to create new keybindings using the **Right Super Key ($\text{Super\_R}$)**—a key often conveniently located near the right-hand side of the keyboard—combined with the navigation keys.

The actions are mapped as follows:

| Key Combination | Action | Description |
| :--- | :--- | :--- |
| **Super + Left** | Copy ($\text{Ctrl}$+$\text{C}$) | Copies the currently selected text to the standard clipboard. |
| **Super + Right** | Paste ($\text{Ctrl}$+$\text{V}$) | Pastes the contents of the standard clipboard. |
| **Super + Up** | Paste Primary Selection | **Simulates the middle mouse click**, pasting the text that was highlighted (Primary Selection). |

### The Critical Fix: Preventing "Stuck" Keys

A common issue when binding system keys (like $\text{Super}$) to execute external scripts in Openbox is that the $\text{Super}$ key can get "stuck" in a pressed state after the script finishes. This is because the $\text{Key Release}$ event is sometimes missed.

The fix is built directly into the script using the `xdotool keyup` command to explicitly release all modifier keys.

-----

## Actions: Implementation Steps

### 1\. Prerequisite Tools

Ensure the necessary command-line utilities for clipboard management and input simulation are installed:

```bash
sudo apt update
sudo apt install xdotool xclip
```

### 2\. Create the Consolidated Bash Script

This single script, which we'll call `shortcut-handler.sh`, handles all three copy/paste actions based on a single argument. It also includes the crucial logic to clear stuck keys.

**File: `/path/to/shortcut-handler.sh`**

```bash
#!/bin/bash

# --- 1. Input Validation ---
ACTION="$1"
if [ -z "$ACTION" ]; then
    echo "Error: No action specified." >&2
    echo "Usage: $0 {copy|paste|middleclick}" >&2
    exit 1
fi

# --- 2. Pre-Execution Key Cleanup ---
# CRITICAL: Virtually release all common modifier keys before proceeding.
# This prevents the next action from failing if a modifier is stuck.
xdotool keyup Super_L Super_R Control_L Control_R Alt_L Alt_R Shift_L Shift_R

# --- 3. Execute Action ---
case "$ACTION" in
    # Simulates standard Ctrl+V (Clipboard Selection)
    paste)
        xdotool key --clearmodifiers Control+v
        ;;

    # Simulates standard Ctrl+C (Clipboard Selection)
    copy)
        xdotool key --clearmodifiers Control+c
        ;;

    # Pastes the Primary Selection (highlighted text), which is more reliable than a simple 'click 2'
    middleclick)
        xclip -out -selection primary | xdotool type --clearmodifiers --delay 1 --file -
        ;;

    # Handle invalid arguments
    *)
        echo "Error: Invalid action '$ACTION'. Use: copy, paste, or middleclick." >&2
        exit 1
        ;;
esac

# --- 4. Post-Execution Key Cleanup ---
# Explicitly release the Super key again to guarantee it's not stuck after execution.
xdotool keyup Super_L keyup Super_R

exit 0
```

**Make the script executable:**

```bash
chmod +x /path/to/shortcut-handler.sh
```

### 3\. Configure Openbox Keybindings

Edit your Openbox configuration file, typically located at `~/.config/openbox/rc.xml`. Add the following keybinds within the `<keyboard>` tags.

**Note:** Openbox uses the letter **`W`** to represent the $\text{Super}$ (Windows) key.

```xml
    <keybind key="W-Left">
      <action name="Execute">
        <command>sh -c '/path/to/shortcut-handler.sh copy'</command>
      </action>
    </keybind>

    <keybind key="W-Right">
      <action name="Execute">
        <command>sh -c '/path/to/shortcut-handler.sh paste'</command>
      </action>
    </keybind>

    <keybind key="W-Up">
      <action name="Execute">
        <command>sh -c '/path/to/shortcut-handler.sh middleclick'</command>
      </action>
    </keybind>
```

### 4\. Reload Openbox

Save the `rc.xml` file and run the following command in a terminal to apply the new shortcuts:

```bash
openbox --reconfigure
```

-----

## Key Takeaways

The development process yielded several important lessons for reliable Openbox keybinding configuration:

  * **Backup is King:** Always back up your `~/.config/openbox/rc.xml` before making changes.
  * **The Shell Wrapper is Essential:** The `<command>` tag in `rc.xml` only accepts a single simple command. To chain commands or pass arguments, you must wrap the entire sequence in an explicit shell call: `sh -c 'command1; command2'`.
  * **Preventing Stuck Keys:** The `xdotool keyup Super_L keyup Super_R` command is the reliable remedy for the $\text{Super}$ key getting stuck after executing a script. Placing it *inside* the script, alongside a pre-execution cleanup, makes the overall solution robust.
  * **Debugging Scripts:** When testing, use simple logging inside the script to confirm it's being executed and to check for errors:
    ```bash
    # Add to the script for debugging (DISABLE WHEN DONE)
    echo "$(date) - Running $ACTION" >> /tmp/openbox-debug.log
    ```

11 March 2025

Edit fstrim.timer configuration [Debian Linux 11]


# Edit fstrim.timer configuration [Debian Linux 11]


There are some guides out there, located in forums. I didn't like very much what I saw.

Here I describe what I did and the results.


### Checking

```bash
$ systemctl cat fstrim.timer
# /lib/systemd/system/fstrim.timer
[Unit]
Description=Discard unused blocks once a week
Documentation=man:fstrim
ConditionVirtualization=!container

[Timer]
OnCalendar=weekly
AccuracySec=1h
Persistent=true
RandomizedDelaySec=6000

[Install]
WantedBy=timers.target

```


### Checking

```bash
$ systemctl status fstrim.timer
● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Sun 2025-03-09 18:22:05 CET; 1 day 14h ago
    Trigger: Mon 2025-03-17 00:38:54 CET; 5 days left
   Triggers: ● fstrim.service
       Docs: man:fstrim

Warning: some journal files were not opened due to insufficient permissions.

```

### Checking

```bash
$ sudo fstrim -av
/home: 120 MiB (125820928 bytes) trimmed on /dev/sda4
/boot: 0 B (0 bytes) trimmed on /dev/sda2
/: 554.1 MiB (581054464 bytes) trimmed on /dev/sda1

```


### Checking

```bash
$ systemctl list-timers fstrim.timer --all
NEXT                        LEFT        LAST                        PASSED  UNIT         ACTIVATES
Mon 2025-03-17 00:09:02 CET 5 days left Mon 2025-03-10 08:31:27 CET 24h ago fstrim.timer fstrim.service

1 timers listed.

```



### Edit the file to change weekly -> daily


```bash
$ sudo vi /etc/systemd/system/fstrim.timer

```

Verify


```bash
$ cat /etc/systemd/system/fstrim.timer
[Unit]
Description=Discard unused blocks once a week
Documentation=man:fstrim
ConditionVirtualization=!container

[Timer]
OnCalendar=daily
AccuracySec=1h
Persistent=true
RandomizedDelaySec=6000

[Install]
WantedBy=timers.target

```


### Reload


#### Correct order

```bash
$ sudo systemctl daemon-reload

```

```bash
$ sudo systemctl restart fstrim.timer

```

#### Note

If you try the `restart` without doing the `daemon-reload` first then you get


```bash
$ sudo systemctl restart fstrim.timer
Warning: The unit file, source configuration file or drop-ins of fstrim.timer changed on disk. Run 'systemctl daemon-reload' to reload units.

```


### Verify



```bash
$ sudo systemctl status fstrim.timer
● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/etc/systemd/system/fstrim.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2025-03-11 09:22:21 CET; 14s ago
    Trigger: Wed 2025-03-12 00:37:18 CET; 15h left
   Triggers: ● fstrim.service
       Docs: man:fstrim

Mar 11 09:22:21 osboxes systemd[1]: Stopped Discard unused blocks once a week.
Mar 11 09:22:21 osboxes systemd[1]: Stopping Discard unused blocks once a week.
Mar 11 09:22:21 osboxes systemd[1]: Started Discard unused blocks once a week.

```

```bash
$ systemctl list-timers fstrim.timer --all
NEXT                        LEFT     LAST                        PASSED  UNIT         ACTIVATES
Wed 2025-03-12 00:37:18 CET 15h left Tue 2025-03-11 09:22:16 CET 36s ago fstrim.timer fstrim.service

1 timers listed.

```

The NEXT changed from next week to next day.


09 March 2025

Enabling storage space reclaim in virtualbox VMs.


Enabling storage space reclaim in virtualbox VMs.

My case:
 - Host: Debian Linux
 - VM: Debian Linux

Recommended reading: https://superuser.com/questions/646559/virtualbox-and-ssds-trim-command-support


# Verify that the Host can trim

```
$ lsblk --discard
NAME   DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sda           0      512B       2G         0
├─sda1        0      512B       2G         0
├─sda2        0      512B       2G         0
├─sda3        0      512B       2G         0
└─sda4        0      512B       2G         0
sr0           0        0B       0B         0
```

If you only see zeros, not good. You need to resolve that first and then come back.


# You can trim on the host

```
$ sudo fstrim -va
/boot/efi: 142 MiB (148893696 bytes) trimmed on /dev/nvme0n1p1
/extra: 0 B (0 bytes) trimmed on /dev/nvme0n1p7
/: 0 B (0 bytes) trimmed on /dev/nvme0n1p6
```


# Do the same on the vm

If you can do the same, then all good.

If the output of `lsblk --discard` shows zeros, then probably the VM does not have the correct configuration.


# Check the vm settings

Run FROM the location of VM files, with the VM powered-off.


```
$ cat *.vbox | grep -C 2 StorageController

 <StorageController name="SATA" type="AHCI" PortCount="1" useHostIOCache="true" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
          <AttachedDevice nonrotational="true" discard="true" type="HardDisk" hotpluggable="false" port="0" device="0">
             <Image uuid="{4e19...80}"/>

```


if your output does not have `discard="true"` then you need to add it.


Note: Adding the text manually on the file, even if the VM is off, WILL NOT HELP. I tested it. You need to unregister & register the vdisk with the proper settings.


Save the uuid as

`$ hd="4e19...80"`


# Get the full path of the vdi file

```
$ cat *.vbox | grep  $hd
        <HardDisk uuid="{4e19...80}" location="file.vdi" format="VDI" type="Normal"/>
          <Image uuid="{4e19...80}"/>
        <HardDisk uuid="{4e19...80}" location="/full/path/to/file.vdi" format="VDI" type="Normal"/>
            <Image uuid="{4e19...80}"/>
```

Save it
`vdipath="/full/path/to/file.vdi"`



# Detach the vdisk from the vm

Use the virtualbox UI for that. Probably you can do it with a command, but I don't provide untested commands.


# Unregister the vdisk from Virtualbox

`$ VBoxManage closemedium disk $hd`



# Re-Register the vdisk with the vm with the needed settings

## List the vms and save the name in the variable `vm`

```
$ VBoxManage list vms
"ThisVM" {8a52....ce4}
```

`$ vm = "8a52....ce4"`


This is the important part

`$ VBoxManage storageattach $vm --storagectl "SATA" --port 0 --device 0 --nonrotational on --discard on --medium $vdipath --type hdd`


You can grep again the .vbox file to verify.


Now power on, and run the `lsblk --discard`, it should be fine.




21 February 2025

AutoKey (for Linux) script for generating XML tags


# Details
when typing:   something#<
trigger is: "#<"
variable: something

# Actions
1. removes the whole word and types:  <something></something>
2. places the cursor in the middle.

It preserves whatever was on the clipboard.


# Code

```python
import time
#from autokey.scripting import *

import time
#from autokey.scripting import *

trigger = "#<"
original_clipboard = clipboard.get_clipboard()

# Move cursor left to remove the trigger
keyboard.send_keys("<backspace>" * len(trigger))

# Select the whole word
keyboard.send_keys("<ctrl>+<shift>+<left>")
time.sleep(0.1)
keyboard.send_keys("<ctrl>+c")
time.sleep(0.1)

selected_text = clipboard.get_clipboard().strip()

if selected_text:
    keyboard.send_keys(f"<{selected_text}></{selected_text}>")
    # Move cursor inside the tags
    keyboard.send_keys("<left>" * (len(selected_text) + 3))


clipboard.fill_clipboard(original_clipboard)
```

# Trigger setup (this is important as it goes along with the code)




# Demo




Hope it helps.


19 February 2025

Moving the vscode configuration folder to RAM transparently.


Note about my setup: Vscode running on linux, remotely against another linux.


# Context

Moving the vscode configuration folder to RAM transparently.

One way, my chosen one, is using profile-sync-daemon (PSD).

I have used it extensively, with 2 browsers.

PSD is like anything-sync-daemon but specialized in browsers.

However, you can make vscode look like a browser with a few steps.

DISCLAIMER: This may not be what you want, it may not work for you. Anything may happen, even data loss. No backups, no brain.

Note: I have PSD up and running well, so I won't cover that.

# 1. Create conf file for vscode

Each "browser" has a little configuration file for PSD to know what to sync and how to call it. Just that.

In `/usr/share/psd/browsers/` you should see a few files like


```
/usr/share/psd/browsers/epiphany
/usr/share/psd/browsers/firefox
/usr/share/psd/browsers/firefox-trunk
/usr/share/psd/browsers/google-chrome
/usr/share/psd/browsers/google-chrome-beta
/usr/share/psd/browsers/google-chrome-unstable
/usr/share/psd/browsers/heftig-aurora
/usr/share/psd/browsers/icecat
/usr/share/psd/browsers/inox
/usr/share/psd/browsers/luakit
/usr/share/psd/browsers/midori
/usr/share/psd/browsers/opera
/usr/share/psd/browsers/opera-beta
/usr/share/psd/browsers/opera-developer
```


Create one

`# touch /usr/share/psd/browsers/vscode`


# 2. Edit it.

Enter on it and add


```
DIRArr[0]="$XDG_CONFIG_HOME/Code"
PSNAME="vscode"
```


In case you didn't know


```
$ echo $XDG_CONFIG_HOME/Code
/home/<user>/.config/Code
```

Save and exit.


Verify

```
$ cat /usr/share/psd/browsers/vscode
DIRArr[0]="$XDG_CONFIG_HOME/Code"
PSNAME="vscode"
```

# 3. Edit psd configuration


Add your "new browser" to your personal conf file located on `/home/<user>/.config/psd/psd.conf`


```
# Before
# BROWSERS="waterfox google-chrome"
# now : added vscode on 2025-02-19
BROWSERS="waterfox google-chrome vscode"
```

Save file.

Close Browsers and VSCode, fully.


At this stage you may want to do

`$ psd preview`

in case it finds any issue.


```
Profile-sync-daemon v6.31 on Debian GNU/Linux 10 (buster)

 Systemd service is currently active.
 Systemd resync-timer is currently active.
 Overlayfs v23 is currently active.

Psd will manage the following per /home/rgarcia/.config/psd/.psd.conf:

...

```


# 4. Restart the service.


`systemctl --user restart psd.service`


Now verify that vscode is now taken care of.

`$ psd preview`


```
$ psd preview
Profile-sync-daemon v6.31 on Debian GNU/Linux 10 (buster)

 Systemd service is currently active.
 Systemd resync-timer is currently active.
 Overlayfs v23 is currently active.

Psd will manage the following per /home/<user>/.config/psd/.psd.conf:

 [ ... info about browsers ... ]

 browser/psname:  vscode/vscode
 owner/group id:  <user>/1000
 sync target:     /home/<user>/.config/Code
 tmpfs dir:       /run/user/1000/<user>-vscode
 profile size:    143M
 overlayfs size:  0
 recovery dirs:   1 <- delete with the c option
  dir path/size:  /home/<user>/.config/<...>-backup-crashrecovery-20250203_203220 (864M)
```

Done.

You Hard-Drive will be very happy, even if it is SSD.


26 January 2025

Configuring Multimedia Keys in LXDE for use in Firefox Add-ons

# Configuring Multimedia Keys in LXDE for use in Firefox Add-ons

## Problem Definition and Environment

In my quest to use multimedia keys on my "Microsoft Curve Keyboard "within the LXDE environment on Debian, I encountered a frustrating challenge. Specifically, I wanted to configure the multimedia keys to work with the "Text Aloud" add-on in Firefox. Firefox only accepts shortcuts using **Alt** or **Control**, so I needed to remap the buttons to other key combinations.

## Extracting Key Codes

To address this issue, I first needed to determine how my multimedia keys were being recognized by the system. I utilized the `xev` command, which allows you to monitor key events in X11. By running the command `xev | grep keycode`, I was able to see the keycodes associated with my multimedia buttons. The output showed that the keys were detected as follows:

```
state 0x10, keycode 121 (keysym 0x1008ff12, XF86AudioMute), same_screen YES,
state 0x10, keycode 122 (keysym 0x1008ff11, XF86AudioLowerVolume), same_screen YES,
state 0x10, keycode 123 (keysym 0x1008ff13, XF86AudioRaiseVolume), same_screen YES,
state 0x10, keycode 172 (keysym 0x1008ff14, XF86AudioPlay), same_screen YES,
```

This output confirmed that the multimedia keys were indeed recognized by the system, and I could proceed to map them to the desired actions.



Another way to extract the codes is



```

$ xbindkeys -mk
Press combination of keys or/and click under the window.
You can use one of the two lines after "NoCommand"
in $HOME/.xbindkeysrc to bind a key.

--- Press "q" to stop. ---
"(Scheme function)"
    m:0x10 + c:172
    Mod2 + XF86AudioPlay
"(Scheme function)"
    m:0x10 + c:122
    Mod2 + XF86AudioLowerVolume
"(Scheme function)"
    m:0x10 + c:121
    Mod2 + XF86AudioMute
"(Scheme function)"
    m:0x10 + c:123
    Mod2 + XF86AudioRaiseVolume
"(Scheme function)"
    m:0x10 + c:24
    Mod2 + q

```



## Initial Attempts and Failures

My first approach involved using `xbindkeys`, a utility that allows you to bind keys to specific commands. I created a configuration file to map the multimedia keys to shortcuts that would invoke the appropriate actions in Firefox. Here's a snippet of my initial configuration:

```bash
"xdotool key Alt+p"
    XF86AudioPlay
"xdotool key Alt+o"
    XF86AudioMute
"xdotool key Alt+comma"
    XF86AudioLowerVolume
"xdotool key Alt+period"
    XF86AudioRaiseVolume
```

To reload I do

`pkill xbindkeys; xbindkeys`





However, despite `xbindkeys` running successfully and detecting the keys, pressing the multimedia buttons did not trigger the expected actions in Firefox or any other application. I tested various combinations and configurations, but nothing seemed to work.



I must say that the `xdotool key Alt+period` command was working well, even within Firefox.


I tested that by doing 

`sleep 2; xdotool key Alt+period` 

on a terminal and then quickly moving the focus to Firefox.




## An Alternative Approach: Using `lxde-rc.xml`

After some troubleshooting, I decided to take a different approach by directly modifying the `lxde-rc.xml` configuration file, which is used by the LXDE desktop environment to manage keyboard shortcuts among other things.

To implement this, I edited the `lxde-rc.xml` file and added keybindings for the multimedia keys. Here's how I initially set it up:

```xml
<keyboard>
    <keybind key="XF86AudioLowerVolume">
        <action name="Execute">
            <command>xdotool key Alt+comma</command>
        </action>
    </keybind>
    <keybind key="XF86AudioRaiseVolume">
        <action name="Execute">
            <command>xdotool key Alt+period</command>
        </action>
    </keybind>
</keyboard>
```

To test if this approach was working correctly, I replaced the `xdotool` command with a simpler command that opens a terminal (`lxterminal`). This way, I could easily observe whether the keybindings were functioning. The modified keybinding looked like this:

```xml
<keybind key="XF86AudioLowerVolume">
    <action name="Execute">
        <command>lxterminal</command>  <!-- For testing -->
    </action>
</keybind>
```

After saving the changes, I reloaded the Openbox configuration by running `openbox --reconfigure`. When I pressed the multimedia key for lowering the volume, a terminal opened, confirming that the keybinding was now functional.

## Conclusion

Mapping multimedia keys in Linux environments often requires persistence and a methodical approach. By extracting keycodes, experimenting with tools like `xbindkeys`, and utilizing the `lxde-rc.xml` file, multimedia keys can be successfully configured for specific applications, such as Firefox add-ons. These steps demonstrate the flexibility and customization options available within Linux desktop environments, making it possible to tailor functionality to individual requirements.


Too Cool for Internet Explorer