# looking-glass-setup-guide.md

> Source: <https://gist.github.com/safwyls/96b6cf4b49e04af2668b7a77502e5ff2>
> Published: 2026-02-10 03:27:19+00:00

# Looking Glass B7 + GPU Passthrough on CachyOS (Hyprland/Wayland)

A complete guide to running a Windows 11 VM with GPU passthrough and Looking Glass on an Arch-based Linux system with a Wayland compositor. This setup allows you to run Windows-only software like Fusion 360 in a native-feeling window on your Linux desktop, with near-zero latency and no compression artifacts.

This guide was written from a real setup session, including every issue encountered and how it was resolved. If you're running into problems, there's a good chance the answer is in here.

---

## System Specifications

| Component | Detail |
|-----------|--------|
| **Host OS** | CachyOS (Arch-based) with Hyprland (Wayland) |
| **Host GPU** | NVIDIA GeForce RTX 3080 Ti |
| **Guest GPU** | NVIDIA GeForce GTX 1080 (passed through to VM) |
| **CPU** | Intel i9-12900K (16 cores, 24 threads) |
| **RAM** | 64 GB total, 32 GB allocated to VM |
| **QEMU** | 6.2+ (JSON-style configuration) |
| **libvirt** | 7.9+ |
| **NVIDIA driver** | 590.48.01 |
| **Looking Glass** | B7 stable release |
| **Target resolution** | 3440×1440 (ultrawide) |

---

## How Looking Glass Works

Before diving into configuration, it helps to understand the architecture.

Looking Glass works by sharing GPU framebuffer data between the Windows VM and your Linux host through shared memory, completely bypassing the normal display output path.

The **host application** runs inside the Windows VM. It uses the Desktop Duplication API (D12 on Windows 10+) to capture frames the GPU is rendering — the same way screen recording works. Instead of encoding those frames into a video stream like RDP or VNC, it writes raw, uncompressed frame data directly into a shared memory region.

That shared memory region is the **IVSHMEM device** — a virtual PCI device that both the VM and the host can access simultaneously. It's a chunk of RAM that exists at the same physical memory addresses for both sides. The KVMFR kernel module manages this on the Linux side, and the IVSHMEM PCI driver handles it on the Windows side.

The **client application** runs on your Linux desktop. It maps that same shared memory region and reads the frame data directly. Because there's no encoding, decoding, or network transport involved, the latency is extremely low — essentially the time it takes to copy pixels from GPU memory into shared memory, plus one DMA transfer.

SPICE runs alongside as a separate channel purely for input — keyboard, mouse, clipboard sync, and audio. The actual video frames never touch SPICE.

---

## Part 0: VFIO GPU Passthrough Setup

This section covers everything needed to get the guest GPU claimed by VFIO and passed through to the VM. If you already have a working passthrough setup, skip to Part 1.

### 0.1 Enable VT-d in BIOS

This is the number one thing people miss. The kernel parameter `intel_iommu=on` does nothing if the CPU feature isn't enabled in firmware.

Reboot into BIOS/UEFI and look for one of these settings (varies by motherboard manufacturer):

- VT-d
- Intel Virtualization Technology for Directed I/O
- IOMMU

Enable it, save, and exit. Also confirm VT-x (CPU virtualization) is enabled while you're there.

### 0.2 Kernel Parameters

CachyOS uses systemd-boot. The correct way to set persistent kernel parameters is through `/etc/sdboot-manage.conf`, **not** by editing boot entries directly (those get overwritten on kernel updates).

```bash
sudo vim /etc/sdboot-manage.conf
```

Find the `LINUX_OPTIONS` line and set:

```bash
LINUX_OPTIONS="intel_iommu=on rd.driver.pre=vfio-pci video=efifb:off"
```

Then regenerate the boot entries:

```bash
sudo sdboot-manage gen
```

Reboot and verify:

```bash
cat /proc/cmdline
```

You should see your parameters in the output.

### Troubleshooting: iommu=pt Causes Empty IOMMU Groups

**Problem:** `ls /sys/kernel/iommu_groups/` shows an empty directory despite VT-d being enabled and `intel_iommu=on` in the kernel parameters.

**Cause:** `iommu=pt` (passthrough mode) can prevent IOMMU groups from being populated. The `dmesg` output will show `Default domain type: Passthrough` — meaning devices aren't being isolated.

**Fix:** Remove `iommu=pt` from your kernel parameters entirely, or change it to `iommu=1`. Despite being commonly recommended in guides, passthrough mode defeats the purpose of VFIO isolation. The `rd.driver.pre=vfio-pci` parameter is what actually matters for early VFIO binding.

### Troubleshooting: Kernel Parameters Disappear After Updates

**Problem:** After a system update, `cat /proc/cmdline` no longer shows your VFIO parameters.

**Cause:** CachyOS regenerates systemd-boot entries during kernel updates, overwriting manual edits to `/boot/loader/entries/*.conf`.

**Fix:** Use `/etc/sdboot-manage.conf` as described above — this is the CachyOS-specific mechanism for persistent kernel parameters. Never edit boot entries directly; always use `LINUX_OPTIONS` in the config file and run `sudo sdboot-manage gen` to apply.

### 0.3 Identify Your GPUs

```bash
lspci -nn | grep -E "VGA|Audio" | grep -i nvidia
```

Example output for a dual-GPU system:

```
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA102 [GeForce RTX 3080 Ti] [10de:2208] (rev a1)
01:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
08:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1080] [10de:1b80] (rev a1)
08:00.1 Audio device [0403]: NVIDIA Corporation GP104 High Definition Audio Controller [10de:10f0] (rev a1)
```

Note the device IDs in brackets. In this case, the GTX 1080 (guest GPU) has IDs `10de:1b80` (GPU) and `10de:10f0` (audio).

### 0.4 Bind the Guest GPU to VFIO

Create `/etc/modprobe.d/vfio.conf`:

```
options vfio-pci ids=10de:1b80,10de:10f0
softdep snd_hda_intel pre: vfio-pci
```

The `softdep` line ensures VFIO loads before the audio driver, which otherwise races to claim the GPU's audio device first.

Add VFIO modules to the initramfs so they load early enough. Edit `/etc/mkinitcpio.conf`:

```
MODULES=(vfio_pci vfio vfio_iommu_type1)
```

Rebuild the initramfs:

```bash
sudo mkinitcpio -P
```

Reboot, then verify both GPU functions are bound to vfio-pci:

```bash
lspci -nnk -s 08:00
```

Expected output:

```
08:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP104 [GeForce GTX 1080] [10de:1b80] (rev a1)
	Kernel driver in use: vfio-pci
08:00.1 Audio device [0403]: NVIDIA Corporation GP104 High Definition Audio Controller [10de:10f0] (rev a1)
	Kernel driver in use: vfio-pci
```

### Troubleshooting: VFIO Not Binding (No Driver Shown)

**Problem:** After configuring everything, `lspci -nnk` shows no driver in use for the GPU.

**Cause:** The VFIO modules aren't in the initramfs, so they don't load early enough.

**Fix:** Verify the modules are actually present:

```bash
sudo lsinitcpio /boot/initramfs-linux-cachyos.img | grep vfio
```

Note: CachyOS names its initramfs differently (`linux-cachyos` or `linux-cachyos-lts`). If the modules aren't listed, re-check `/etc/mkinitcpio.conf` and rebuild with `sudo mkinitcpio -P`.

### 0.5 Create the VM and Add the GPU

Create a Windows 11 VM in virt-manager with Q35 machine type and UEFI firmware (OVMF). Then add the GPU:

1. Add Hardware → PCI Host Device → select `0000:08:00.0` (GPU)
2. Add Hardware → PCI Host Device → select `0000:08:00.1` (Audio)

### 0.6 CPU Configuration (Critical)

The CPU mode configuration is surprisingly important for VFIO + shared memory compatibility. Using the wrong mode causes QEMU crashes with `vfio_container_region_add` errors when shared memory devices are present.

**Working configuration:**

```xml
<cpu mode='host-model' check='none'>
  <topology sockets='1' dies='1' cores='18' threads='1'/>
</cpu>
```

**What doesn't work:**

- `host-passthrough` with `migratable='on'` — causes memory mapping conflicts between the GPU passthrough and IVSHMEM device, crashing QEMU
- `host-passthrough` without `migratable` — may also conflict depending on QEMU version

The `host-model` mode has libvirt select a compatible CPU model rather than exposing exact CPU features, which avoids the memory region overlap. The `migratable` attribute must be removed entirely (not set to `'off'` — just absent).

Without the topology block, Windows will see an incorrect CPU layout (e.g., 2 sockets instead of 18 cores). The topology line tells Windows exactly how the vCPUs are organized. Note that vCPU allocation is shared, not reserved — your host can still use all 24 cores; the VM just won't use more than 18 at once.

### 0.7 Bridge Networking

For the VM to be accessible on your LAN (required for SPICE, and useful for general connectivity), set up a network bridge:

```bash
sudo nmcli connection add type bridge ifname br0 con-name br0
sudo nmcli connection add type ethernet ifname enp6s0 master br0
sudo nmcli connection up br0
```

Replace `enp6s0` with your actual ethernet interface (check with `ip link`).

Then configure the VM to use the bridge in its XML:

```xml
<interface type='bridge'>
  <mac address='52:54:00:xx:xx:xx'/>
  <source bridge='br0'/>
  <model type='e1000e'/>
</interface>
```

The VM will get its own IP from your router's DHCP server.

### 0.8 Install NVIDIA Drivers in the Guest

Once the VM boots with the passed-through GPU, install the NVIDIA drivers manually from nvidia.com. GeForce Experience often fails in VMs due to hardware detection checks, so download the standalone driver instead:

1. Go to https://www.nvidia.com/Download/index.aspx
2. Select GeForce → 10 Series → GTX 1080 → Windows 11
3. Download and install the driver directly

### Why Not Moonlight/Sunshine?

Before arriving at Looking Glass, Moonlight/Sunshine was tested as an alternative. The results for this particular use case (CAD work at 3440×1440) were not satisfactory:

- At 165 FPS (native refresh rate), massive compression artifacts appeared — NVENC on the GTX 1080 can't keep up at that frame rate
- At 60 FPS with 150 Mbps bitrate, image quality was acceptable but mouse input felt sluggish — unacceptable for precision CAD work in Fusion 360
- HEVC encoding wasn't available (Moonlight client-side decoding issues on CachyOS)
- Even with optimized settings, the encode/decode pipeline adds perceptible latency compared to raw framebuffer sharing

For gaming or media consumption, Moonlight/Sunshine is excellent. For daily-driver CAD/productivity work where you need pixel-perfect rendering and zero input lag, Looking Glass is the correct tool.

### Approaches That Were Tried and Abandoned

**GL.iNet Comet Pro (IP KVM):** Hardware-based HDMI capture from the GTX 1080. Works for remote access and BIOS-level troubleshooting, but adds its own compression and latency. Useful as a fallback but not a primary display solution for productivity work.

**Intel iGPU passthrough (GVT-d):** The i9-12900K has a UHD 770 iGPU, but 12th gen Alder Lake does not support GVT-g (mediated/shared GPU). GVT-d requires full passthrough, meaning the host loses the iGPU entirely (must blacklist i915). Combined with known Code 43 errors, missing VBIOS issues, and special OpROM requirements, this was not worth pursuing when the GTX 1080 is vastly more capable for Fusion 360.

---

---

## Part 1: KVMFR Kernel Module

The KVMFR module creates a character device (`/dev/kvmfr0`) that provides a high-performance shared memory interface between host and guest.

### 1.1 Build and Install via DKMS

Clone the Looking Glass B7 stable release (not git master — the client and host versions must match):

```bash
cd ~/source
git clone --branch B7 --single-branch https://github.com/gnif/LookingGlass.git looking-glass-B7
cd looking-glass-B7/module
```

Install via DKMS so the module rebuilds automatically on kernel updates:

```bash
sudo dkms install "."
```

### 1.2 Calculate Shared Memory Size

The formula from the Looking Glass docs:

```
width × height × 4 × 2 = frame bytes
round up to nearest power of 2 in MB
```

For 3440×1440:

```
3440 × 1440 × 4 × 2 = 39,628,800 bytes ≈ 37.8 MB
Round up to nearest power of 2 → 64 MB
```

However, **64 MB can be tight for ultrawide resolutions**. We used 128 MB to provide headroom and avoid any frame corruption issues.

### 1.3 Configure the Module

Create `/etc/modprobe.d/kvmfr.conf`:

```
options kvmfr static_size_mb=128
```

Create `/etc/modules-load.d/kvmfr.conf` to auto-load at boot:

```
kvmfr
```

### 1.4 Set Up udev Rules

Create `/etc/udev/rules.d/99-kvmfr.rules`:

```
SUBSYSTEM=="kvmfr", OWNER="your_username", GROUP="kvm", MODE="0660"
```

Replace `your_username` with your actual Linux username.

Reload and trigger:

```bash
sudo udevadm control --reload-rules
sudo udevadm trigger
```

### 1.5 Load the Module

```bash
sudo modprobe kvmfr
```

Verify it's working:

```bash
ls -l /dev/kvmfr0
```

You should see a character device (permissions start with `c`) owned by your user:

```
crw-rw---- 1 your_username kvm 508, 0 Feb  8 15:32 /dev/kvmfr0
```

### Troubleshooting: /dev/kvmfr0 Permissions

**Problem:** After loading the module, `/dev/kvmfr0` is owned by root with mode 600.

**Cause:** The udev rule didn't trigger, or the module was reloaded after creating the device (which creates a fresh device node without re-triggering udev).

**Fix:** Apply permissions manually for the current session, then fix the udev rule for persistence:

```bash
sudo chown your_username:kvm /dev/kvmfr0
sudo chmod 660 /dev/kvmfr0
```

### Troubleshooting: /dev/kvmfr0 is a Regular File

**Problem:** `ls -l /dev/kvmfr0` shows a regular file (permissions start with `-`) instead of a character device (`c`).

**Cause:** The VM was started *before* the KVMFR module was loaded. QEMU creates a regular file as a fallback.

**Fix:** Stop the VM, remove the file, load the module, then start the VM:

```bash
virsh destroy win11
sudo rm /dev/kvmfr0
sudo modprobe kvmfr
virsh start win11
```

---

## Part 2: libvirt / QEMU Configuration

### 2.1 Add QEMU Namespace to Domain XML

The `<domain>` tag needs the QEMU namespace for the commandline passthrough:

```xml
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
```

This must be done in the same `virsh edit` session as adding the QEMU commandline block.

### 2.2 Add KVMFR Shared Memory Device

Add this block at the bottom of your VM XML, just before `</domain>`:

```xml
<qemu:commandline>
  <qemu:arg value="-device"/>
  <qemu:arg value="{'driver':'ivshmem-plain','id':'shmem0','memdev':'looking-glass'}"/>
  <qemu:arg value="-object"/>
  <qemu:arg value="{'qom-type':'memory-backend-file','id':'looking-glass','mem-path':'/dev/kvmfr0','size':134217728,'share':true}"/>
</qemu:commandline>
```

The size is in bytes: 128 MB × 1024 × 1024 = 134,217,728.

> **Important:** This is JSON-style syntax required for QEMU 6.2+ / libvirt 7.9+. Using the older comma-separated syntax on newer versions will cause a "PCI slot not available" error.

### 2.3 Remove Conflicting shmem Block

If you previously tried the standard shared memory approach, **remove** any `<shmem>` block like this:

```xml
<!-- REMOVE THIS if present -->
<shmem name='looking-glass'>
  <model type='ivshmem-plain'/>
  <size unit='M'>128</size>
</shmem>
```

The QEMU commandline block replaces it. Having both will cause conflicts.

### 2.4 Disable Memory Ballooning

Change the memballoon device from `virtio` to `none`:

```xml
<memballoon model="none"/>
```

The virtio balloon driver causes performance issues with VFIO passthrough.

### 2.5 Remove USB Tablet Device

Looking Glass is incompatible with absolute pointing devices. **Remove** the USB tablet:

```xml
<!-- REMOVE THIS -->
<input type="tablet" bus="usb">
  <address type="usb" bus="0" port="1"/>
</input>
```

Without removing this, you'll get mouse input issues and the Looking Glass FAQ explicitly calls this out.

### 2.6 Configure SPICE for Input

SPICE provides keyboard, mouse, and clipboard sync to the VM (video is handled by Looking Glass, not SPICE):

```xml
<graphics type="spice" autoport="yes">
  <listen type="address" address="127.0.0.1"/>
</graphics>
```

Install SPICE guest tools inside Windows for clipboard sync and proper integration.

### 2.7 Configure cgroups for /dev/kvmfr0

Edit `/etc/libvirt/qemu.conf` and find the `cgroup_device_acl` block. Uncomment it and add `/dev/kvmfr0`:

```
cgroup_device_acl = [
    "/dev/null", "/dev/full", "/dev/zero",
    "/dev/random", "/dev/urandom",
    "/dev/ptmx", "/dev/kvm",
    "/dev/kvmfr0"
]
```

Then restart libvirtd:

```bash
sudo systemctl restart libvirtd
```

### Troubleshooting: "Permission denied" on /dev/kvmfr0

**Problem:** VM fails to start with `can't open backing store /dev/kvmfr0 for guest RAM: Permission denied`.

**Cause:** cgroups policy is blocking QEMU from accessing the device, even if file permissions are correct.

**Fix:** Ensure `/dev/kvmfr0` is in the `cgroup_device_acl` list in `/etc/libvirt/qemu.conf` and restart libvirtd.

---

## Part 3: Building the Looking Glass Client

> **Critical: The client and host must be built from the same release.** If you install the B7 host in Windows, you must build the B7 client on Linux. Mixing versions (e.g., a B7 host with a git master client, or vice versa) will cause connection failures or protocol mismatches. Always download the same tagged release for both sides.

### 3.1 Install Dependencies (Arch/CachyOS)

```bash
sudo pacman -S cmake gcc libgl libegl fontconfig spice-protocol make nettle \
  pkgconf binutils libxi libxinerama libxss libxcursor libxpresent \
  libxkbcommon wayland-protocols ttf-dejavu libsamplerate
```

### 3.2 Build the Client

Build from the **B7 stable release** source (must match the Windows host version):

```bash
cd ~/source/looking-glass-B7/client
mkdir build
cd build
cmake ..
make -j$(nproc)
```

Verify X11 support is enabled (required for XWayland rendering with OpenGL):

```bash
grep ENABLE_X11 CMakeCache.txt
# Should show: ENABLE_X11:BOOL=ON
```

### 3.3 Install

```bash
sudo cp looking-glass-client /usr/local/bin/
```

---

## Part 4: Windows Guest Configuration

### 4.1 Install Looking Glass Host

Download the B7 host installer from the Looking Glass releases page and install it in the Windows VM.

The host runs as a Windows service under the SYSTEM account. This is important because it needs to capture the desktop even at the login screen.

Host application location: `C:\Program Files\Looking Glass (host)\looking-glass-host.exe`
Log file: `%ProgramData%\Looking Glass (host)\looking-glass-host.txt`

### 4.2 Install IddSampleDriver (Virtual Display)

If you don't have a physical monitor connected to the passed-through GPU, you need a virtual display driver. IddSampleDriver creates a virtual monitor that the GPU renders to, which Looking Glass then captures.

Download IddSampleDriver from its GitHub repository and install it in the Windows VM. Configure it with your desired resolution (e.g., 3440×1440).

> **Critical:** Do not set the IddSampleDriver display as the *primary* display if you're still using SPICE or RDP for initial setup. If the GPU can't output to the virtual display before Looking Glass is connected, you may lose access to the VM entirely. Set it as a secondary display first, then make it primary only after confirming Looking Glass captures it.

### Troubleshooting: Locked Out After IddSampleDriver Misconfiguration

**Problem:** Set IddSampleDriver as primary display, can't see the login screen via SPICE or any other method.

**Fix:** Mount the VM's disk from the Linux host and remove the driver:

```bash
# Ensure VM is stopped
virsh destroy win11

# Mount the QCOW2 disk
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 /path/to/win11.qcow2
sudo fdisk -l /dev/nbd0  # Identify the Windows NTFS partition (usually p3)
sudo mount -t ntfs-3g -o rw,remove_hiberfile /dev/nbd0p3 /mnt
```

The `remove_hiberfile` option is needed because Windows Fast Startup leaves the filesystem dirty. Navigate to the IddSampleDriver installation directory and remove or rename the driver files:

```bash
# Remove or rename the driver directory
sudo mv /mnt/path/to/IddSampleDriver /mnt/path/to/IddSampleDriver.bak
```

Unmount and disconnect:

```bash
sudo umount /mnt
sudo qemu-nbd --disconnect /dev/nbd0
```

Start the VM again, and it should fall back to the SPICE display.

---

## Part 5: Running Looking Glass

### 5.1 Basic Launch

```bash
looking-glass-client
```

The client will automatically find `/dev/kvmfr0` and connect to the shared memory region. SPICE integration provides keyboard/mouse input.

### 5.2 The Critical Fix: OpenGL Renderer on NVIDIA + Wayland

**This is the most important section in this guide.** If you're running NVIDIA + Hyprland (or any Wayland compositor), you will almost certainly encounter flickering black rectangles in the Looking Glass output.

**Problem:** Occasional flickering black boxes/rectangles appearing in the Looking Glass window.

**Root cause:** The default EGL renderer has a compositing incompatibility with the NVIDIA driver under Wayland. This is not a Looking Glass bug, not a D12 capture issue, and not an IddSampleDriver problem.

**Solution:** Force the OpenGL renderer instead of EGL.

Create `~/.looking-glass-client.ini`:

```ini
[app]
renderer=opengl
```

That's it. The OpenGL renderer completely eliminates the flickering without any performance penalty.

**What doesn't fix it (so you don't waste time):**

- Disabling D12 damage tracking (`trackDamage=false` in the Windows host config)
- Switching to DXGI capture
- Downgrading egl-wayland
- Disabling explicit sync (`__NV_DISABLE_EXPLICIT_SYNC=1`)
- Enabling double buffering (`egl:doubleBuffer=true`)
- Disabling DMA (`app:allowDMA=no`)
- Unsetting `WAYLAND_DISPLAY` (this forces XWayland, which works but is unnecessary)

The OpenGL renderer is the clean, correct fix.

### 5.3 VM Autostart

To have the Windows VM start automatically at boot:

```bash
virsh autostart win11
```

To disable later:

```bash
virsh autostart --disable win11
```

---

## Complete Configuration Reference

### VM XML Key Sections

```xml
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  <name>win11</name>
  <memory unit='GiB'>32</memory>
  <vcpu placement='static'>18</vcpu>

  <!-- CPU mode: host-model WITHOUT migratable (critical for VFIO + shmem) -->
  <cpu mode='host-model' check='none'>
    <topology sockets='1' dies='1' cores='18' threads='1'/>
  </cpu>
	
  <!-- GPU Passthrough -->
  <hostdev mode='subsystem' type='pci' managed='yes'>
    <source>
      <address domain='0x0000' bus='0x08' slot='0x00' function='0x0'/>
    </source>
  </hostdev>
  <hostdev mode='subsystem' type='pci' managed='yes'>
    <source>
      <address domain='0x0000' bus='0x08' slot='0x00' function='0x1'/>
    </source>
  </hostdev>

  <!-- SPICE for input only -->
  <graphics type="spice" autoport="yes">
    <listen type="address" address="127.0.0.1"/>
  </graphics>

  <!-- No memory ballooning -->
  <memballoon model="none"/>

  <!-- NO USB tablet device -->

  <!-- KVMFR shared memory -->
  <qemu:commandline>
    <qemu:arg value="-device"/>
    <qemu:arg value="{'driver':'ivshmem-plain','id':'shmem0','memdev':'looking-glass'}"/>
    <qemu:arg value="-object"/>
    <qemu:arg value="{'qom-type':'memory-backend-file','id':'looking-glass','mem-path':'/dev/kvmfr0','size':134217728,'share':true}"/>
  </qemu:commandline>
</domain>
```

### Host Configuration Files

| File | Contents |
|------|----------|
| `/etc/modprobe.d/kvmfr.conf` | `options kvmfr static_size_mb=128` |
| `/etc/modules-load.d/kvmfr.conf` | `kvmfr` |
| `/etc/udev/rules.d/99-kvmfr.rules` | `SUBSYSTEM=="kvmfr", OWNER="your_username", GROUP="kvm", MODE="0660"` |
| `/etc/libvirt/qemu.conf` | Add `/dev/kvmfr0` to `cgroup_device_acl` |
| `~/.looking-glass-client.ini` | `[app]` / `renderer=opengl` |

### Windows Guest Configuration

| Component | Location |
|-----------|----------|
| Looking Glass host | `C:\Program Files\Looking Glass (host)\looking-glass-host.exe` |
| Host log | `%ProgramData%\Looking Glass (host)\looking-glass-host.txt` |
| Capture method | D12 (default, fastest for Windows 10+) |
| IddSampleDriver | Virtual display at 3440×1440 |
| SPICE guest tools | Installed for clipboard sync |

---

## Troubleshooting Quick Reference

| Symptom | Cause | Fix |
|---------|-------|-----|
| VM won't start: "Permission denied" on `/dev/kvmfr0` | cgroups blocking access | Add `/dev/kvmfr0` to `cgroup_device_acl` in `qemu.conf`, restart libvirtd |
| `/dev/kvmfr0` owned by root | udev rule not applied | `sudo chown user:kvm /dev/kvmfr0 && sudo chmod 660 /dev/kvmfr0`; fix udev rule |
| `/dev/kvmfr0` is regular file, not char device | VM started before module loaded | Stop VM, `rm /dev/kvmfr0`, `modprobe kvmfr`, start VM |
| VM won't start: "PCI slot not available" | Using legacy QEMU syntax on new QEMU | Use JSON-style `qemu:commandline` syntax |
| Flickering black boxes in LG output | EGL renderer + NVIDIA + Wayland | Set `renderer=opengl` in `~/.looking-glass-client.ini` |
| No display in Looking Glass | Host not running / wrong display captured | Check host log; ensure IddSampleDriver is configured |
| Locked out of VM after display change | IddSampleDriver set as primary with no way to view it | Mount QCOW2 via qemu-nbd, remove driver files |
| Mouse issues in Looking Glass | USB tablet device present | Remove `<input type="tablet">` from VM XML |
| No clipboard sync | SPICE tools not installed | Install SPICE guest tools in Windows |

---

## Useful Commands

```bash
# Check KVMFR module status
lsmod | grep kvmfr
dmesg | grep kvmfr

# Check device permissions
ls -la /dev/kvmfr0

# VM management
virsh start win11
virsh destroy win11     # force stop
virsh shutdown win11    # graceful shutdown
virsh autostart win11

# Mount VM disk for emergency access
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 /path/to/win11.qcow2
sudo mount -t ntfs-3g -o rw,remove_hiberfile /dev/nbd0p3 /mnt
# ... do your work ...
sudo umount /mnt
sudo qemu-nbd --disconnect /dev/nbd0

# Launch Looking Glass with verbose output
looking-glass-client -d
```

