6565
6666
6767# FUTURE: this should be moved to backend with new API changes
68- DEV_TYPES = ["block" , "usb" , "mic" ]
68+ DEV_TYPES = ["block" , "usb" , "mic" , "webcam" ]
6969
7070
7171class DeviceMenu (Gtk .Menu ):
@@ -112,6 +112,7 @@ def __init__(self, app_name, qapp, dispatcher):
112112 self .vms : Set [backend .VM ] = set ()
113113 self .dispvm_templates : Set [backend .VM ] = set ()
114114 self .parent_ports_to_hide = []
115+ self .cameras_to_hide = []
115116 self .active_usbvms : Set [backend .VM ] = set ()
116117 self .dormant_usbvms : Set [backend .VM ] = set ()
117118 self .dev_update_queue : Set = set ()
@@ -180,6 +181,13 @@ def __init__(self, app_name, qapp, dispatcher):
180181 self .dispatcher .add_handler (
181182 "domain-feature-delete:internal" , self .update_internal_feature
182183 )
184+ self .dispatcher .add_handler (
185+ "domain-feature-set:" + backend .FEATURE_RESOLUTION , self .update_resolution
186+ )
187+ self .dispatcher .add_handler (
188+ "domain-feature-delete:" + backend .FEATURE_RESOLUTION ,
189+ self .update_resolution ,
190+ )
183191
184192 for feature in [backend .FEATURE_HIDE_CHILDREN , backend .FEATURE_ATTACH_WITH_MIC ]:
185193
@@ -274,13 +282,17 @@ def device_added(self, vm, _event, device):
274282 if dev .parent :
275283 for potential_parent in self .devices .values ():
276284 if potential_parent .port == dev .parent :
285+ # hide parents of webcams
286+ if dev .device_class == "webcam" :
287+ self .cameras_to_hide .append (dev .parent )
288+ potential_parent .hide_this_device = True
277289 potential_parent .has_children = True
278290 break
279291
280292 # connect with mic
281293 mic_feature = vm .features .get (backend .FEATURE_ATTACH_WITH_MIC , "" ).split (" " )
282294 if dev_id in mic_feature :
283- microphone = self .devices .get ("dom0:mic:dom0:mic::m000000" , None )
295+ microphone = self .devices .get ("mic: dom0:mic:dom0:mic::m000000" , None )
284296 microphone .devices_to_attach_with_me .append (dev )
285297 dev .devices_to_attach_with_me = [microphone ]
286298
@@ -306,7 +318,7 @@ def device_removed(self, vm, _event, port):
306318 # we never knew the device anyway
307319 return
308320
309- microphone = self .devices .get ("dom0:mic:dom0:mic::m000000" , None )
321+ microphone = self .devices .get ("mic: dom0:mic:dom0:mic::m000000" , None )
310322
311323 self .emit_notification (
312324 _ ("Device removed" ),
@@ -318,6 +330,12 @@ def device_removed(self, vm, _event, port):
318330 microphone .devices_to_attach_with_me .remove (dev )
319331 if dev .port in self .parent_ports_to_hide :
320332 self .parent_ports_to_hide .remove (dev .port )
333+ if dev .parent in self .cameras_to_hide :
334+ for potential_parent in self .devices .values ():
335+ if potential_parent .port == dev .parent :
336+ potential_parent .hide_this_device = False
337+ break
338+ self .cameras_to_hide .remove (dev .parent )
321339 del self .devices [dev_id ]
322340
323341 def initialize_dev_data (self ):
@@ -359,6 +377,12 @@ def initialize_dev_data(self):
359377 # we have no permission to access VM's devices
360378 continue
361379
380+ # hide parents of webcams
381+ for device in self .devices .values ():
382+ if device .device_class == "webcam" :
383+ if device .parent :
384+ self .cameras_to_hide .append (device .parent )
385+
362386 def device_assigned (self , vm , _event , device , ** _kwargs ):
363387 dev_id = backend .Device .id_from_device (device )
364388 if dev_id not in self .devices :
@@ -407,7 +431,7 @@ def update_single_feature(self, _vm, _event, feature, value=None, oldvalue=None)
407431 add = new - old
408432 remove = old - new
409433
410- microphone = self .devices .get ("dom0:mic:dom0:mic::m000000" , None )
434+ microphone = self .devices .get ("mic: dom0:mic:dom0:mic::m000000" , None )
411435
412436 for dev_name in remove :
413437 if feature == backend .FEATURE_ATTACH_WITH_MIC :
@@ -436,6 +460,29 @@ def update_single_feature(self, _vm, _event, feature, value=None, oldvalue=None)
436460 self .parent_ports_to_hide .append (dev .port )
437461 self .hide_child_devices (dev .port , False )
438462
463+ def update_resolution (self , vm , _event , feature , value = None , oldvalue = None ):
464+ # pylint: disable=unused-argument
465+ res_dict = {}
466+ if value :
467+ for word in value .split (" " ):
468+ try :
469+ k , v = word .split ("=" )
470+ res_dict [k ] = v
471+ except ValueError :
472+ # the feature is malformed, ignore it
473+ res_dict = {}
474+
475+ for device in self .devices .values ():
476+ if (
477+ device .device_class == "webcam"
478+ and device .backend_domain .name == vm .name
479+ ):
480+ resolution = res_dict .get (device .id_string , None )
481+ if resolution :
482+ device .options ["format" ] = resolution
483+ elif "format" in device .options :
484+ del device .options ["format" ]
485+
439486 def vm_unpaused (self , vm , _event , ** _kwargs ):
440487 wrapped_vm = backend .VM (vm )
441488 try :
@@ -473,7 +520,7 @@ def initialize_features(self, *_args, **_kwargs):
473520 """
474521 domains = self .qapp .domains
475522
476- microphone = self .devices .get ("dom0:mic:dom0:mic::m000000" , None )
523+ microphone = self .devices .get ("mic: dom0:mic:dom0:mic::m000000" , None )
477524 # clear existing feature mappings
478525 for dev in self .devices .values ():
479526 dev .devices_to_attach_with_me = []
@@ -487,13 +534,21 @@ def initialize_features(self, *_args, **_kwargs):
487534 mic_feature = domain .features .get (
488535 backend .FEATURE_ATTACH_WITH_MIC , False
489536 )
537+ if not mic_feature :
538+ continue
490539 except qubesadmin .exc .QubesDaemonAccessError :
491540 continue
492- if isinstance (mic_feature , str ):
493- mic_dev_strings .extend (
494- [dev for dev in mic_feature .split (" " ) if dev ]
495- )
496-
541+ for dev in mic_feature .split (" " ):
542+ if not dev :
543+ continue
544+ try :
545+ _class , vm_name , _rest = dev .split (":" , 2 )
546+ if vm_name != domain .name :
547+ continue
548+ mic_dev_strings .append (dev )
549+ except ValueError :
550+ # malformed name
551+ pass
497552 microphone .devices_to_attach_with_me = []
498553
499554 for dev in mic_dev_strings :
@@ -519,6 +574,9 @@ def initialize_features(self, *_args, **_kwargs):
519574 dev .show_children = False
520575
521576 self .hide_child_devices ()
577+ for dev in self .devices .values ():
578+ if dev .port in self .cameras_to_hide :
579+ dev .hide_this_device = True
522580
523581 def hide_child_devices (
524582 self , parent_port : Optional [str ] = None , state : bool = False
0 commit comments