1#!/usr/local/bin/python3.8 2 3import platform 4import subprocess 5import shlex 6import os 7import re 8import threading 9from json import loads 10 11from SettingsWidgets import SidePage 12from xapp.GSettingsWidgets import * 13 14 15def killProcess(process): 16 process.kill() 17 18 19def getProcessOut(command): 20 timeout = 2.0 # Timeout for any subprocess before aborting it 21 22 lines = [] 23 p = subprocess.Popen(command, stdout=subprocess.PIPE) 24 timer = threading.Timer(timeout, killProcess, [p]) 25 timer.start() 26 while True: 27 line = p.stdout.readline() 28 if not line: 29 break 30 if line != '': 31 lines.append(line.decode('utf-8')) 32 timer.cancel() 33 return lines 34 35 36def getGraphicsInfos(): 37 cards = {} 38 count = 0 39 envpath = os.environ["PATH"] 40 os.environ["PATH"] = envpath + ":/usr/local/sbin:/usr/sbin:/sbin" 41 for card in getProcessOut(("lspci")): 42 if not "VGA" in card: 43 continue 44 cardId = card.split()[0] 45 cardName = None 46 for line in getProcessOut(("lspci", "-v", "-s", cardId)): 47 if line.startswith(cardId): 48 cardName = (line.split(":")[2].split("(rev")[0].strip()) 49 50 if cardName: 51 cards[count] = (cardName) 52 count += 1 53 os.environ["PATH"] = envpath 54 return cards 55 56 57def getDiskSize(): 58 disksize = 0 59 try: 60 out = getProcessOut(("lsblk", "--json", "--output", "size", "--bytes", "--nodeps")) 61 jsonobj = loads(''.join(out)) 62 except Exception: 63 return _("Unknown size"), False 64 65 for blk in jsonobj['blockdevices']: 66 disksize += int(blk['size']) 67 68 return disksize, (len(jsonobj['blockdevices']) > 1) 69 70 71def getProcInfos(): 72 # For some platforms, 'model name' will no longer take effect. 73 # We can try our best to detect it, but if all attempts failed just leave it to be "Unknown". 74 # Source: https://github.com/dylanaraps/neofetch/blob/6dd85d67fc0d4ede9248f2df31b2cd554cca6c2f/neofetch#L2163 75 cpudetect = ("model name", "Hardware", "Processor", "cpu model", "chip type", "cpu type") 76 infos = [ 77 ("/proc/cpuinfo", [("cpu_name", cpudetect), ("cpu_siblings", ("siblings",)), ("cpu_cores", ("cpu cores",))]), 78 ("/proc/meminfo", [("mem_total", ("MemTotal",))]) 79 ] 80 81 result = {} 82 for (proc, pairs) in infos: 83 for line in getProcessOut(("cat", proc)): 84 for (key, start) in pairs: 85 for item in start: 86 if line.startswith(item): 87 result[key] = line.split(':', 1)[1].strip() 88 break 89 if "cpu_name" not in result: 90 result["cpu_name"] = _("Unknown CPU") 91 if "mem_total" not in result: 92 result["mem_total"] = _("Unknown size") 93 return result 94 95 96def createSystemInfos(): 97 procInfos = getProcInfos() 98 infos = [] 99 arch = platform.machine().replace("_", "-") 100 try: 101 (memsize, memunit) = procInfos['mem_total'].split(" ") 102 memsize = float(memsize) 103 except ValueError: 104 memsize = procInfos['mem_total'] 105 memunit = "" 106 processorName = procInfos['cpu_name'].replace("(R)", "\u00A9").replace("(TM)", "\u2122") 107 if 'cpu_cores' in procInfos: 108 processorName = processorName + " \u00D7 " + procInfos['cpu_cores'] 109 110 if os.path.exists("/etc/linuxmint/info"): 111 args = shlex.split("awk -F \"=\" '/GRUB_TITLE/ {print $2}' /etc/linuxmint/info") 112 title = subprocess.check_output(args).decode('utf-8').rstrip("\n") 113 infos.append((_("Operating System"), title)) 114 elif os.path.exists("/etc/arch-release"): 115 contents = open("/etc/arch-release", 'r').readline().split() 116 title = ' '.join(contents[:2]) or "Arch Linux" 117 infos.append((_("Operating System"), title)) 118 elif os.path.exists("/etc/manjaro-release"): 119 contents = open("/etc/manjaro-release", 'r').readline().split() 120 title = ' '.join(contents[:2]) or "Manjaro Linux" 121 infos.append((_("Operating System"), title)) 122 else: 123 import distro 124 s = '%s (%s)' % (' '.join(distro.linux_distribution()), arch) 125 # Normalize spacing in distribution name 126 s = re.sub(r'\s{2,}', ' ', s) 127 infos.append((_("Operating System"), s)) 128 if 'CINNAMON_VERSION' in os.environ: 129 infos.append((_("Cinnamon Version"), os.environ['CINNAMON_VERSION'])) 130 infos.append((_("Linux Kernel"), platform.release())) 131 infos.append((_("Processor"), processorName)) 132 if memunit == "kB": 133 infos.append((_("Memory"), '%.1f %s' % ((float(memsize)/(1024*1024)), _("GiB")))) 134 else: 135 infos.append((_("Memory"), procInfos['mem_total'])) 136 137 diskSize, multipleDisks = getDiskSize() 138 if (multipleDisks): 139 diskText = _("Hard Drives") 140 else: 141 diskText = _("Hard Drive") 142 try: 143 infos.append((diskText, '%.1f %s' % ((diskSize / (1000*1000*1000)), _("GB")))) 144 except: 145 infos.append((diskText, diskSize)) 146 cards = getGraphicsInfos() 147 for card in cards: 148 infos.append((_("Graphics Card"), cards[card])) 149 150 return infos 151 152 153class Module: 154 name = "info" 155 category = "hardware" 156 comment = _("Display system information") 157 158 def __init__(self, content_box): 159 keywords = _("system, information, details, graphic, sound, kernel, version") 160 sidePage = SidePage(_("System Info"), "cs-details", keywords, content_box, module=self) 161 self.sidePage = sidePage 162 163 def on_module_selected(self): 164 if not self.loaded: 165 print("Loading Info module") 166 167 infos = createSystemInfos() 168 169 page = SettingsPage() 170 self.sidePage.add_widget(page) 171 172 settings = page.add_section(_("System info")) 173 174 for (key, value) in infos: 175 widget = SettingsWidget() 176 widget.set_spacing(40) 177 labelKey = Gtk.Label.new(key) 178 widget.pack_start(labelKey, False, False, 0) 179 labelKey.get_style_context().add_class("dim-label") 180 labelValue = Gtk.Label.new(value) 181 labelValue.set_selectable(True) 182 labelValue.set_line_wrap(True) 183 widget.pack_end(labelValue, False, False, 0) 184 settings.add_row(widget) 185 186 if os.path.exists("/usr/local/bin/upload-system-info"): 187 widget = SettingsWidget() 188 189 spinner = Gtk.Spinner(visible=True) 190 button = Gtk.Button(label=_("Upload system information"), 191 tooltip_text=_("No personal information included"), 192 always_show_image=True, 193 image=spinner) 194 button.connect("clicked", self.on_button_clicked, spinner) 195 widget.pack_start(button, True, True, 0) 196 settings.add_row(widget) 197 198 def on_button_clicked(self, button, spinner): 199 200 try: 201 subproc = Gio.Subprocess.new(["upload-system-info"], Gio.SubprocessFlags.NONE) 202 subproc.wait_check_async(None, self.on_subprocess_complete, spinner) 203 spinner.start() 204 except GLib.Error as e: 205 print("upload-system-info failed to run: %s" % e.message) 206 207 def on_subprocess_complete(self, subproc, result, spinner): 208 spinner.stop() 209 210 try: 211 success = subproc.wait_check_finish(result) 212 except GLib.Error as e: 213 print("upload-system-info failed: %s" % e.message) 214