def __init__(self): super().__init__() self.controller = Controller() self.displays = Displays() if len(self.displays) == 0: print('Could not find supported displays') quit() self.sensors = LuminanceSourceManager() self.sensors.add_source_type(LuminanceIIO) self.sensors.add_source_type(LuminanceMQTT, { 'topic': MQTT_TOPIC, 'host': MQTT_HOST }) manual_parameters = self.controller.get_range() manual_parameters.update({'value': self.displays.get_brightness()}) self.sensors.add_source_type(LuminanceManual, manual_parameters) self.sensors.activate(DEFAULT_SENSOR) self.menu = self.construct_menu() self.menu_visible = False self.setContextMenu(self.menu) self.activated.connect(lambda reason: self.action_click(reason, self)) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.main_control) self.timer.start(1000) if MQTT_PUBLISH is True: self.mqtt_publisher = LuminanceMQTT(name="mqttp", path=MQTT_TOPIC, host=MQTT_HOST)
def __init__(self): self.sensors = Sensors() self.displays = Displays() self.gate = Gate()
class Track(): def __init__(self): self.sensors = Sensors() self.displays = Displays() self.gate = Gate() def startRace(self): self.displays.clear() self.gate.release() self.sensors.start() def stopRace(self): # No need to keep sensing when no race is happening self.sensors.stop() # Make sure we can reset the gate self.gate.reset() self.displays.displayTimes(self.sensors.getTimes()) return self.sensors.getTimes() def getTimes(self): return self.sensors.getTimes() def test(self): self.gate.release() time.sleep(2) self.gate.reset() self.displays.displayHex([0xba5e, 0xba11, 0x0]) time.sleep(2) self.displays.displayHex([0x0, 0xcafe, 0xbabe]) time.sleep(2) self.displays.displayHex([0xdead, 0x0, 0xbeef]) time.sleep(2) self.displays.clear() time.sleep(1) currentTime = time.time() while (currentTime + 10.0) > time.time(): self.displays.displayTimes(self.sensors.getState()) self.displays.clear()
def __init__(self): mirror_logger.info("Started Mirrorcast") self.indicator = appindicator.Indicator.new( "mirrorMenu", os.path.abspath('/opt/mirrorcast/mirrorcast_tray.png'), appindicator.IndicatorCategory.SYSTEM_SERVICES) self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) #A string so we know what the user is doing self.state = "stopped" self.menu = gtk.Menu() #Set up menu item_start = gtk.MenuItem('Start Mirroring') item_start.connect('activate', self.start) self.menu.append(item_start) #Media Sub Menu self.media_sub = gtk.Menu() item_media = gtk.MenuItem('Play Media (Experimental)') item_media.set_submenu(self.media_sub) item_file = gtk.MenuItem('Media File') item_file.connect('activate', self.file) self.media_sub.append(item_file) item_dvd = gtk.MenuItem('Play DVD') item_dvd.connect('activate', self.dvd) self.media_sub.append(item_dvd) item_youtube = gtk.MenuItem('Youtube URL') item_youtube.connect('activate', self.youtube) self.media_sub.append(item_youtube) self.menu.append(item_media) item_freeze = gtk.MenuItem('Freeze') item_freeze.connect('activate', self.freeze) self.menu.append(item_freeze) item_update = gtk.MenuItem('Update Mirrorcast') item_update.connect('activate', self.update) self.menu.append(item_update) item_quit = gtk.MenuItem('Quit') item_quit.connect('activate', self.quit) self.menu.append(item_quit) sep = gtk.SeparatorMenuItem() self.menu.append(sep) self.outputSub = gtk.Menu() output = gtk.MenuItem("Cast To") output.set_submenu(self.outputSub) '''Recievers/Hosts Menu''' self.hosts = hosts() #Varaibles for sorting receivers into sub-menus self.list_receivers = [] self.sortedMenu = [] sortSub = [] subitems = [] sortInd = 0 sortInd2 = 0 #Add receivers to menu self.list_receivers.append(gtk.RadioMenuItem('None')) for ind, i in enumerate(self.hosts.receivers): #allow user to sort their receivers into sublists if i['aspect'] == "sub": self.sortedMenu.append(gtk.Menu()) sortSub.append(gtk.MenuItem(i['host'])) sortSub[sortInd].set_submenu(self.sortedMenu[sortInd]) self.outputSub.append(sortSub[sortInd]) sortInd = sortInd + 1 elif sortInd > 0: try: subitems.append( gtk.RadioMenuItem(str(i['host']), group=self.subitems[sortInd2 - 1])) except: subitems.append( gtk.RadioMenuItem(str(i['host']), group=self.list_receivers[0])) subitems[sortInd2].connect('toggled', self.hosts.set_receiver, subitems[sortInd2].get_label()) self.sortedMenu[sortInd - 1].append(subitems[sortInd2]) sortInd2 = sortInd2 + 1 else: self.list_receivers.append( gtk.RadioMenuItem(str(i['host']), group=self.list_receivers[ind - 1])) for i in self.list_receivers: self.outputSub.append(i) i.connect('toggled', self.hosts.set_receiver, i.get_label()) self.list_receivers[0].set_active(True) self.Display = Displays() self.displaysSub = gtk.Menu() displays = gtk.MenuItem("Select Display to Mirror") displays.set_submenu(self.displaysSub) self.list_displays = [] #Add displays/monitors to menu for ind, i in enumerate(self.Display.monitors): if ind != 0: self.list_displays.append( gtk.RadioMenuItem(str(i[0]), group=self.list_displays[ind - 1])) else: self.list_displays.append( gtk.RadioMenuItem(self.Display.monitors[0][0])) for i in self.list_displays: self.displaysSub.append(i) i.connect('toggled', self.Display.set_display, i.get_label()) self.list_displays[0].set_active(True) self.menu.append(output) self.menu.append(displays) self.menu.show_all() self.indicator.set_menu(self.menu) self.sound = Audio() self.ffmpeg = None self.vlc = None self.sleep = dbus_listen(item_start, self.ffmpeg)
class TrayMenu: def __init__(self): mirror_logger.info("Started Mirrorcast") self.indicator = appindicator.Indicator.new( "mirrorMenu", os.path.abspath('/opt/mirrorcast/mirrorcast_tray.png'), appindicator.IndicatorCategory.SYSTEM_SERVICES) self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) #A string so we know what the user is doing self.state = "stopped" self.menu = gtk.Menu() #Set up menu item_start = gtk.MenuItem('Start Mirroring') item_start.connect('activate', self.start) self.menu.append(item_start) #Media Sub Menu self.media_sub = gtk.Menu() item_media = gtk.MenuItem('Play Media (Experimental)') item_media.set_submenu(self.media_sub) item_file = gtk.MenuItem('Media File') item_file.connect('activate', self.file) self.media_sub.append(item_file) item_dvd = gtk.MenuItem('Play DVD') item_dvd.connect('activate', self.dvd) self.media_sub.append(item_dvd) item_youtube = gtk.MenuItem('Youtube URL') item_youtube.connect('activate', self.youtube) self.media_sub.append(item_youtube) self.menu.append(item_media) item_freeze = gtk.MenuItem('Freeze') item_freeze.connect('activate', self.freeze) self.menu.append(item_freeze) item_update = gtk.MenuItem('Update Mirrorcast') item_update.connect('activate', self.update) self.menu.append(item_update) item_quit = gtk.MenuItem('Quit') item_quit.connect('activate', self.quit) self.menu.append(item_quit) sep = gtk.SeparatorMenuItem() self.menu.append(sep) self.outputSub = gtk.Menu() output = gtk.MenuItem("Cast To") output.set_submenu(self.outputSub) '''Recievers/Hosts Menu''' self.hosts = hosts() #Varaibles for sorting receivers into sub-menus self.list_receivers = [] self.sortedMenu = [] sortSub = [] subitems = [] sortInd = 0 sortInd2 = 0 #Add receivers to menu self.list_receivers.append(gtk.RadioMenuItem('None')) for ind, i in enumerate(self.hosts.receivers): #allow user to sort their receivers into sublists if i['aspect'] == "sub": self.sortedMenu.append(gtk.Menu()) sortSub.append(gtk.MenuItem(i['host'])) sortSub[sortInd].set_submenu(self.sortedMenu[sortInd]) self.outputSub.append(sortSub[sortInd]) sortInd = sortInd + 1 elif sortInd > 0: try: subitems.append( gtk.RadioMenuItem(str(i['host']), group=self.subitems[sortInd2 - 1])) except: subitems.append( gtk.RadioMenuItem(str(i['host']), group=self.list_receivers[0])) subitems[sortInd2].connect('toggled', self.hosts.set_receiver, subitems[sortInd2].get_label()) self.sortedMenu[sortInd - 1].append(subitems[sortInd2]) sortInd2 = sortInd2 + 1 else: self.list_receivers.append( gtk.RadioMenuItem(str(i['host']), group=self.list_receivers[ind - 1])) for i in self.list_receivers: self.outputSub.append(i) i.connect('toggled', self.hosts.set_receiver, i.get_label()) self.list_receivers[0].set_active(True) self.Display = Displays() self.displaysSub = gtk.Menu() displays = gtk.MenuItem("Select Display to Mirror") displays.set_submenu(self.displaysSub) self.list_displays = [] #Add displays/monitors to menu for ind, i in enumerate(self.Display.monitors): if ind != 0: self.list_displays.append( gtk.RadioMenuItem(str(i[0]), group=self.list_displays[ind - 1])) else: self.list_displays.append( gtk.RadioMenuItem(self.Display.monitors[0][0])) for i in self.list_displays: self.displaysSub.append(i) i.connect('toggled', self.Display.set_display, i.get_label()) self.list_displays[0].set_active(True) self.menu.append(output) self.menu.append(displays) self.menu.show_all() self.indicator.set_menu(self.menu) self.sound = Audio() self.ffmpeg = None self.vlc = None self.sleep = dbus_listen(item_start, self.ffmpeg) #the following function is run when the user clicks "Start/Stop Mirroring" def start(self, w): notify.init("mirrorMenu") mirror_logger.info("Detected Audio Device: " + str(self.sound.audioDev)) if w.get_label() == 'Start Mirroring': #If the user did not select a receiver if self.hosts.receiver == "None": notify.init("mirrorMenu") notify.Notification.new("Error", "You did not select a receiver", None).show() return notify.Notification.new( "Connecting to Receiver", "Attempting to establish connection to " + self.hosts.receiver, None).show() mirror_logger.info("User is trying to connect to " + self.hosts.receiver) #If we cannot connect to the receiver if self.connect("play,") == False: notify.init("mirrorMenu") notify.Notification.new( "Connection Error", "Could not connect to" + self.hosts.receiver + ". please try again and if problem persists then please contact your system administrator.", None).show() mirror_logger.warning("Failed to connect to " + self.hosts.receiver) return #Create and start loop that checks if receiver can still be reached mirror_logger.info("User connected to " + self.hosts.receiver) w.set_label("Stop Mirroring") self.start_casting() #Start a loop that will keep checking if the client can still reach the server connection = threading.Thread(target=self.alive, args=[w]) connection.start() elif w.get_label() == 'Stop Mirroring': self.state = "stopped" self.sound.audio(False) self.Display.display(False, self.hosts.aspect) if self.ffmpeg != None: if self.ffmpeg.poll() == None: self.ffmpeg.terminate() w.set_label('Start Mirroring') return def start_casting(self): res = self.Display.resolution self.sleep.sleep = False #If receiver is set to display 4:3 and the client is 16:9 then change screen resoltion to 1024x768 if (self.hosts.aspect == "wide" or self.hosts.aspect == "16:9" or self.hosts.aspect == "16:10") and self.Display.get_ratio(res) == "16:9": self.hosts.aspect = "16:9" else: self.hosts.aspect = "4:3" if self.Display.get_ratio(self.Display.resolution) != "4:3": res = "1024x768" try: subprocess.call("xrandr --output " + self.Display.type + " --mode 1024x768", shell=True) except: mirror_logger.warning( "Could now change screen resolution to 4:3") notify.Notification.new( "Resolution Error", "Failed to change screen resolution to match receiver.", None).show() return self.sound.audio(True) #Start encoding and sending the stream to the receiver self.state = "casting" time.sleep(1) display = os.environ['DISPLAY'] #get display of current user self.ffmpeg = subprocess.Popen([ "ffmpeg", "-loglevel", "warning", "-f", "pulse", "-ac", "2", "-i", "default", "-async", "1", "-f", "x11grab", "-r", "25", "-s", str(res), "-i", str(display) + "+" + str(int(self.Display.xoffset)) + "," + str(self.Display.yoffset), "-aspect", self.hosts.aspect, "-vcodec", "libx264", "-pix_fmt", "yuv420p", "-tune", "zerolatency", "-preset", "ultrafast", "-vf", "scale=" + str(res).replace('x', ':'), "-f", "mpegts", "udp://" + self.hosts.receiver + ":8090" ], stdout=subprocess.PIPE) self.sound.monitor_audio() notify.Notification.new( "Connection Established", "Connection to " + self.hosts.receiver + " established.", None).show() def alive(self, w): mirror_logger.info("Sending Alive Packets") timestamp = time.localtime() timeout = 10 retries = 1 i = 0 command = "active," + socket.gethostname() while True: try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((self.hosts.receiver, 8092)) sock.settimeout(None) #If the user's computer is going to sleep if self.state == "stopped" or self.sleep.sleep == True: mirror_logger.info("User stopped casting") command = "stop," + socket.gethostname() sock.send(command.encode('ascii')) sock.close() return if self.state == "freeze": logging.info("User frooze their screen") command = "freeze," + socket.gethostname() sock.send(command.encode('ascii')) status = sock.recv(1024) if status.decode('ascii') == "paused": self.ffmpeg.terminate() w.set_label('Start Mirroring') notify.init("mirrorMenu") notify.Notification.new( "Freezed", "You have frozen your current desktop, click Start Mirroring to resume", None).show() self.state = "stopped" time.sleep(1) self.sound.audio(False) self.Display.display(False, self.hosts.aspect) sock.close() return sock.send(command.encode('ascii')) status = sock.recv(1024) if status.decode('ascii') == "ok": timestamp = time.localtime() sock.close() except: #time.sleep(1) if ( time.mktime(time.localtime()) - time.mktime(timestamp) ) >= timeout and self.state != "stopped" and self.sleep.sleep != True: i = i + 1 if i == 1: mirror_logger.warning("Attempting to reconnect to " + self.hosts.receiver) if self.ffmpeg.poll() == None: self.ffmpeg.terminate() notify.Notification.new( "Reconnecting", "Connection to " + self.hosts.receiver + " has been lost. Attempting to reconnect.", None).show() time.sleep(2) if self.connect("play,") == True: mirror_logger.info("Reconnected to " + self.hosts.receiver) self.start_casting() i = 0 if i == retries: self.state = "stopped" w.set_label('Start Mirroring') notify.init("mirrorMenu") notify.Notification.new( "Connection Lost", "Connection to " + self.hosts.receiver + " timed out.", None).show() mirror_logger.warning( "Connection Lost: Connection to " + self.hosts.receiver + " timed out.") return def quit(self, w): self.sound.audio(False) self.state = "stopped" self.Display.display(False, self.hosts.aspect) #kill ffmpeg incase user forgot to click "stop" if self.ffmpeg != None: if self.ffmpeg.poll() == None: self.ffmpeg.terminate() gtk.main_quit() def freeze(self, w): if self.state == "stopped": notify.init("mirrorMenu") notify.Notification.new( "Not Mirroring", "To freeze your screen you need to Start Mirroring.", None).show() return self.state = "freeze" return def update(self, w): if self.state == "casting": notify.init("mirrorMenu") notify.Notification.new( "Cannot Update", "Please stop mirroring before you try to update", None).show() return subprocess.call("/opt/mirrorcast/mirrorcast-autoupdater.sh", shell=True) gtk.main_quit() return def file(self, w): if self.state == "casting": notify.init("mirrorMenu") notify.Notification.new( "Error", "Please stop mirroring before you try to use this feature", None).show() else: if self.hosts.receiver == "None": notify.init("mirrorMenu") notify.Notification.new("Error", "Please select a receiving device", None).show() return if self.connect("media,") == False: notify.init("mirrorMenu") notify.Notification.new( "Connection Error", "Could not connect to" + self.hosts.receiver + ". please try again and if problem persists then please contact your system administrator.", None).show() mirror_logger.warning("Failed to connect to " + self.hosts.receiver) return mirror_logger.info("User connected to " + self.hosts.receiver + " to play media file") select = Tk() select.withdraw() types = [("Video Files", ("*.mp4", "*.avi", "*.mov", "*.mkv", "*.flv", "*.mpeg", "*.mpg", "*.wmv")), ("All files", "*.*")] file = askopenfilename(filetypes=types) select.destroy() print(file) if file == () or file == None or file == "": return if self.vlc != None: if self.vlc.poll() == None: self.vlc.terminate() subprocess.Popen([ "vlc", "-q", "file://" + file, "--sout-avcodec-strict=-2", "--sout=#transcode{vcodec=h264,vb=2000,scale=Auto}:duplicate{dst=http{mux=ts,dst=:8090/video},dst=display}", ":sout-keep" ], stdout=subprocess.PIPE) time.sleep(2) self.send_cmd("media-start,") ui = mediaui(self.hosts.receiver) def dvd(self, w): if self.state == "casting": notify.init("mirrorMenu") notify.Notification.new( "Error", "Please stop mirroring before you try to use this feature", None).show() else: if self.hosts.receiver == "None": notify.init("mirrorMenu") notify.Notification.new("Error", "Please select a receiving device", None).show() return if self.connect("media,") == False: notify.init("mirrorMenu") notify.Notification.new( "Connection Error", "Could not connect to" + self.hosts.receiver + ". please try again and if problem persists then please contact your system administrator.", None).show() mirror_logger.warning("Failed to connect to " + self.hosts.receiver) return mirror_logger.info("User connected to " + self.hosts.receiver + " to stream DVD") if self.vlc != None: if self.vlc.poll() == None: self.vlc.terminate() self.vlc = subprocess.Popen([ "vlc", "-q", "dvdsimple://", "--sout-avcodec-strict=-2", "--sout-x264-preset", "ultrafast", "--sout=#transcode{vcodec=h264,vb=2000,scale=Auto}:duplicate{dst=http{mux=ts,dst=:8090/video},dst=display}", ":sout-keep" ], stdout=subprocess.PIPE) time.sleep(2) self.send_cmd("media-start,") ui = mediaui(self.hosts.receiver) def youtube(self, w): if self.state == "casting": notify.init("mirrorMenu") notify.Notification.new( "Error", "Please stop mirroring before you try to use this feature", None).show() else: if self.hosts.receiver == "None": notify.init("mirrorMenu") notify.Notification.new("Error", "Please select a receiving device", None).show() if self.connect("tu-media,") == False: notify.init("mirrorMenu") notify.Notification.new( "Connection Error", "Could not connect to" + self.hosts.receiver + ". please try again and if problem persists then please contact your system administrator.", None).show() mirror_logger.warning("Failed to connect to " + self.hosts.receiver) return ui = tubeui(self.hosts.receiver) def connect(self, cmd): command = cmd + socket.gethostname() try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((self.hosts.receiver, 8092)) sock.settimeout(None) sock.send(command.encode('ascii')) while True: status = sock.recv(8024) #if server returns busy then some one else is already using this receiver if status.decode('ascii') == "busy": notify.init("mirrorMenu") mirror_logger.info("User attempted to connect to " + self.hosts.receiver + " but receiver was busy") notify.Notification.new( "Error", "Sorry some one else is already connected to this receiver, please try again later.", None).show() sock.close() return False #If client succeeds in connecting to receiver elif status.decode('ascii') == "ready": mirror_logger.info("Server is ready") break sock.close() except: return False if cmd == "play,": self.state = "casting" return True def send_cmd(self, cmd): command = cmd + socket.gethostname() try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((self.hosts.receiver, 8092)) sock.settimeout(None) sock.send(command.encode('ascii')) sock.close() except: return False
class ZenDisplay(QtWidgets.QSystemTrayIcon): """System tray icon class""" def __init__(self): super().__init__() self.controller = Controller() self.displays = Displays() if len(self.displays) == 0: print('Could not find supported displays') quit() self.sensors = LuminanceSourceManager() self.sensors.add_source_type(LuminanceIIO) self.sensors.add_source_type(LuminanceMQTT, { 'topic': MQTT_TOPIC, 'host': MQTT_HOST }) manual_parameters = self.controller.get_range() manual_parameters.update({'value': self.displays.get_brightness()}) self.sensors.add_source_type(LuminanceManual, manual_parameters) self.sensors.activate(DEFAULT_SENSOR) self.menu = self.construct_menu() self.menu_visible = False self.setContextMenu(self.menu) self.activated.connect(lambda reason: self.action_click(reason, self)) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.main_control) self.timer.start(1000) if MQTT_PUBLISH is True: self.mqtt_publisher = LuminanceMQTT(name="mqttp", path=MQTT_TOPIC, host=MQTT_HOST) def toggle_menu(self): """Toggle context menu visibility""" if self.menu_visible: self.menu.hide() else: self.menu.popup(QtGui.QCursor.pos()) self.menu_visible = not self.menu_visible def construct_menu(self): """Construct a menu from the sitemap""" menu = QtWidgets.QMenu() self.construct_menu_displays(menu) self.construct_menu_sensors(menu) # Create quit button menu.addSeparator() action_quit = menu.addAction("Quit") action_quit.triggered.connect(lambda _: quit()) return menu def construct_menu_displays(self, parent): """Create submenu for displays""" display_menu = parent.addMenu('Displays') for display in self.displays: action = display_menu.addAction(display['name']) action.setCheckable(True) action.setChecked(display['use']) did = display['id'] action.triggered.connect( lambda state, did=did: self.displays.set_active(did, state)) def construct_menu_sensors(self, parent): """Create submenu for sensors""" sensor_menu = parent.addMenu('Sensors') sensor_group = QtWidgets.QActionGroup(sensor_menu, exclusive=True) for sensor in self.sensors: action = sensor_menu.addAction(sensor.name + " (" + sensor.path + ")") action.setCheckable(True) if sensor.uid == self.sensors.get_active(): action.setChecked(True) sid = sensor.uid action.triggered.connect( lambda state, sid=sid: self.sensors.activate(sid)) sensor_group.addAction(action) def main_control(self): """Main control function, sets display brightness dynamically""" luminance = self.sensors.get_luminance() current_brightness = self.displays.get_brightness() recommended_brightness = self.controller.recommend_brightness( luminance, current_brightness) self.setToolTip('Brightness: ' + str(recommended_brightness) + '%') self.displays.set_brightness(recommended_brightness) if MQTT_PUBLISH is True: self.mqtt_publisher.publish(luminance) def event(self, event): """Event handler for QEvent objects""" if event.type() == QtCore.QEvent.Wheel: new_value = None if event.angleDelta().y() < 0: new_value = self.sensors[self.sensors.get_active()].decrease() else: new_value = self.sensors[self.sensors.get_active()].increase() if new_value is not None and self.supportsMessages(): self.showMessage('Brightness', str(new_value) + '%', msecs=500) return True return False @classmethod @QtCore.pyqtSlot() def action_click(cls, reason, tray): """Toggle menu on click""" if reason in [ QtWidgets.QSystemTrayIcon.Trigger, QtWidgets.QSystemTrayIcon.Context ]: tray.toggle_menu()