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
1 change: 1 addition & 0 deletions icons/bluetooth.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/camera.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/card-sim.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/circuit-board.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/clapperboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/cpu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/dock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/gpu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/keyboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/memory-stick.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/monitor.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/mouse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/speaker.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/usb.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 94 additions & 1 deletion qubesmanager/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import datetime
from qubesadmin.tools import QubesArgumentParser
from qubesadmin import device_protocol
from qubesadmin.device_protocol import DeviceCategory
from qubesadmin import utils as admin_utils
from qubesadmin.tools import qvm_start
import qubesadmin.exc
Expand Down Expand Up @@ -159,6 +160,23 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
)
)

device_icons = {
DeviceCategory.Network: "networking",
DeviceCategory.Keyboard: "keyboard",
DeviceCategory.Mouse: "mouse",
DeviceCategory.Camera: "camera",
DeviceCategory.Audio: "speaker",
DeviceCategory.Multimedia: "clapperboard",
DeviceCategory.Storage: "storage",
DeviceCategory.Bluetooth: "Bluetooth",
DeviceCategory.Smart_Card_Readers: "card-sim",
DeviceCategory.Display: "monitor",
DeviceCategory.Memory: "memory-stick",
DeviceCategory.Docking_Station: "dock",
DeviceCategory.Processor: "cpu",
DeviceCategory.PCI_USB: "usb",
}

# pylint: disable=too-many-positional-arguments
def __init__(
self, vm_name, init_page="basic", qapp=None, qubesapp=None, parent=None
Expand Down Expand Up @@ -244,6 +262,17 @@ def __init__(
self.current_strict_reset_list = []
self.new_strict_reset_list = []
self.define_strict_reset_devices()
self.device_radio_buttons = {
self.dev_network: DeviceCategory.Network,
self.dev_display: DeviceCategory.Display,
self.dev_audio: DeviceCategory.Audio,
self.dev_storage: DeviceCategory.Storage,
self.dev_usb_controller: DeviceCategory.PCI_USB,
}
self.dev_search.textChanged.connect(self.filter_devices)
for button in self.device_radio_buttons:
button.toggled.connect(self.filter_devices)
self.dev_other.toggled.connect(self.filter_devices)

####### services tab
self.__init_services_tab__()
Expand Down Expand Up @@ -1493,6 +1522,8 @@ def bootmode_changed(self):
######## devices tab
def __init_devices_tab__(self):
self.dev_list = multiselectwidget.MultiSelectWidget(self)
self.dev_list.selected_list.setSpacing(5)
self.dev_list.available_list.setSpacing(5)
self.dev_list.change_labels(
available="Available devices",
selected="Devices always connected to this qube",
Expand All @@ -1507,7 +1538,7 @@ def __init_devices_tab__(self):
.devices["pci"]
.get_exposed_devices()
if dev.interfaces[0].category
!= device_protocol.DeviceCategory.PCI_Bridge
!= DeviceCategory.PCI_Bridge
)
attached = list(
self.vm.devices["pci"].get_assigned_devices(required_only=True)
Expand All @@ -1530,6 +1561,15 @@ def __init__(self, dev, unknown=False, parent=None):
name += " (unknown)"
self.setText(name)
self.dev = dev
intfs = list({i.category for i in dev.interfaces})
if len(intfs) != 1 or intfs[0] not in VMSettingsWindow.device_icons:
self.setIcon(QtGui.QIcon(":/circuit-board"))
else:
self.setIcon(
QtGui.QIcon(
":/{}".format(VMSettingsWindow.device_icons[intfs[0]])
)
)

for dev in dom0_devs:
if any(attached_dev.matches(dev) for attached_dev in attached):
Expand Down Expand Up @@ -1658,6 +1698,59 @@ def strict_reset_button_pressed(self):
)
device_list_window.exec()

def filter_devices(self):
keywords = [word for word in self.dev_search.text().split(" ") if word]
for i in range(self.dev_list.available_list.count()):
device = self.dev_list.available_list.item(i)
hide = False
for button, category in self.device_radio_buttons.items():
if button.isChecked():
hide = True
for i in device.dev.interfaces:
if i.category == category:
hide = False
break
if self.dev_other.isChecked():
hide = True
for i in device.dev.interfaces:
if i.category not in self.device_radio_buttons.values():
hide = False
break
for word in keywords:
if not re.search(
word,
device.text(),
re.IGNORECASE
):
hide = True
break
device.setHidden(hide)
for i in range(self.dev_list.selected_list.count()):
device = self.dev_list.selected_list.item(i)
hide = False
for button, category in self.device_radio_buttons.items():
if button.isChecked():
hide = True
for i in device.dev.interfaces:
if i.category == category:
hide = False
break
if self.dev_other.isChecked():
hide = True
for i in device.dev.interfaces:
if i.category not in self.device_radio_buttons.values():
hide = False
break
for word in keywords:
if not re.search(
word,
device.text(),
re.IGNORECASE
):
hide = True
break
device.setHidden(hide)

######## applications tab

def refresh_apps_button_pressed(self):
Expand Down
32 changes: 32 additions & 0 deletions qubesmanager/tests/test_vm_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2044,3 +2044,35 @@ def test_603_virtmode_limitation(settings_fixture):
assert "PVH" not in available_virtmodes
assert "HVM (current)" in available_virtmodes
assert "PV" in available_virtmodes


@check_errors
@pytest.mark.parametrize("settings_fixture", ["test-pci-dev"], indirect=True)
def test_604_device_filter(settings_fixture):
settings_window, page, vm_name = settings_fixture
vm = settings_window.qubesapp.domains[vm_name]
settings_window.dev_search.setText("usb")
settings_window.dev_usb_controller.setChecked(True)

for i in range(settings_window.dev_list.available_list.count()):
item = settings_window.dev_list.available_list.item(i)
assert item.isHidden() != ("USB" in item.text())

for i in range(settings_window.dev_list.selected_list.count()):
item = settings_window.dev_list.selected_list.item(i)
assert item.isHidden() != ("USB" in item.text())

settings_window.dev_search.setText("")
settings_window.dev_other.setChecked(True)

for i in range(settings_window.dev_list.available_list.count()):
item = settings_window.dev_list.available_list.item(i)
if not item.isHidden():
for i in item.dev.interfaces:
assert i.category not in settings_window.device_radio_buttons.values()

for i in range(settings_window.dev_list.selected_list.count()):
item = settings_window.dev_list.selected_list.item(i)
if not item.isHidden():
for i in item.dev.interfaces:
assert i.category not in settings_window.device_radio_buttons.values()
14 changes: 14 additions & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,19 @@
<file alias="fast-rewind">icons/fast-rewind.svg</file>
<file alias="step-back">icons/step-back.svg</file>
<file alias="step-forward">icons/step-forward.svg</file>
<file alias="bluetooth">icons/bluetooth.svg</file>
<file alias="camera">icons/camera.svg</file>
<file alias="card-sim">icons/card-sim.svg</file>
<file alias="circuit-board">icons/circuit-board.svg</file>
<file alias="clapperboard">icons/clapperboard.svg</file>
<file alias="cpu">icons/cpu.svg</file>
<file alias="dock">icons/dock.svg</file>
<file alias="gpu">icons/gpu.svg</file>
<file alias="keyboard">icons/keyboard.svg</file>
<file alias="memory-stick">icons/memory-stick.svg</file>
<file alias="monitor">icons/monitor.svg</file>
<file alias="mouse">icons/mouse.svg</file>
<file alias="speaker">icons/speaker.svg</file>
<file alias="usb">icons/usb.svg</file>
</qresource>
</RCC>
97 changes: 97 additions & 0 deletions ui/settingsdlg.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,103 @@ The qube must be running to disable seamless mode. This setting is not persisten
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutAppDevicesFilter">
<item>
<widget class="QLabel" name="labelDevicesFilter">
<property name="text">
<string>Filter:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="dev_search"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutAppDevicesClass">
<item>
<widget class="QLabel" name="labelDevicesClass">
<property name="text">
<string>Class:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_all">
<property name="text">
<string>All</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_network">
<property name="text">
<string>Network</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/networking</normaloff>:/networking</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_display">
<property name="text">
<string>Display</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/monitor</normaloff>:/monitor</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_audio">
<property name="text">
<string>Audio</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/speaker</normaloff>:/speaker</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_storage">
<property name="text">
<string>Storage</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/storage</normaloff>:/storage</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_usb_controller">
<property name="text">
<string>USB Controller</string>
</property>
<property name="icon">
<iconset resource="../resources.qrc">
<normaloff>:/usb</normaloff>:/usb</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dev_other">
<property name="text">
<string>Other</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
Expand Down