From cd1e29b0af053f38580bbace2041badeacf84faa Mon Sep 17 00:00:00 2001 From: Harald Hope Date: Tue, 22 Feb 2022 15:58:37 -0800 Subject: [PATCH] Just as 3.3.10 > 3.3.11 were a huge set of CPU upgrades, including significant internal refactors, so too is 3.3.13 a significant Graphics upgrade, featuring significant upgrades to Wayland (and Xvesa/TinyX!) support, and allowing for much more granular output controls. The legacy -Ga showing Display/Screen/Monitors is now split apart, and can now work for some features in and out of display. This upgrade should be of significant interest to any Wayland using distro, as well as the tiny Xvesa based distros like TinyCore, Slitaz, and Puppy. -------------------------------------------------------------------------------- NOTE TO MAINTAINERS AND PACKAGERS: If you had Cpanel::JSON::XS or JSON::XS Perl modules as dependencies, you can remove those, inxi now can use JSON::PP, which is in Core Modules since Perl 5.14 (unless for some reason your distro removed that module from Core Modules). Basically inxi will simply look for whichever of the 3 is installed, and use that one. -------------------------------------------------------------------------------- KNOWN ISSUES: 1. The free drivers for xorg like amdgpu, modesetting, alter the the internal kernel IDs for monitors/gfx device ports, which is somewhat bizarre since the ideal role of any ID is to be an identifier that always works. Due to this situation, inxi has to map the kernel ids to the x driver monitor IDs in order to show the advanced monitor data, like model: mapped: and modes:. This may not always work as expected since if the mapping fails, the data will fail to match to the monitors. While not enough data is in to make any conclusions, hoping that this issue does not exist on Wayland compositors. -------------------------------------------------------------------------------- BUGS: 1. Not sure if this was a bug, but I believe RAM vendor ID matches would never have generated results, and might have generated errors. That's corrected as part of Code fix 1. -------------------------------------------------------------------------------- FIXES: 1. Tiny indentation level issue, for -Ga, Monitor was not set to be a container for its data. This would only impact -y 1 or json and xml output cases, and would be subtle, but it was an oversight. 2. Small fix for monitor dimensions, failed to switch the mm dimensions for monitors placed in a vertical, portait mode, instead of standard landscape mode. Now switches mm x and y if that is detected, which corrects dpi as well. 3. For Xvesa: * Show vesa as display driver, Xvesa == vesa, dugh,lol. * Show better Interface and Screen resolution data missing messages. * See FIX 5 for adding in display-ID:. * Show TinX Xvesa string for server data, not just Xvesa. 4. For Wayland, which currently has no EGL support in inxi, if no glxinfo present, show EGL Wayland specific Messsage: for advanced EGL data, not the generic glxinfo that were shown previously. 5. Display was relying on xdpyinfo or a Wayland environmental variable to set display-ID:, now falls back always to $ENV{DISPLAY} if nothing else was found and that exists. I hadn't realized how much was depending on those x tools, which many people never had installed in the first place. This also supplies that for Xvesa as well, which has features that need the Display-ID to use. 6. Intel family 6, model 17h, supposed to be yorkfield, was penryn, fixed. 7. Small fix for remove_duplicates, it was not case insensitive so missed things like DELL Dell in strings. 8. Failed to detect or get Xfree86 X server version number. -------------------------------------------------------------------------------- ENHANCEMENTS: 1. Extensive Graphics Upgrades: * -Gxx Devices: For some gpus / drivers, show vram total and used for -Gxx. amdgpu supports this, I believe it's the only one, but don't know for sure. * -Gxx Devices: (Linux only): Show active, off (connected but disabled, like a closed laptop screen with attached moniitor), and empty ports on devices. Not tested for USB yet. * -Gxx Devices: Show device ports (like VGA-1, DVI-I-1, HDMI-A-1), active, off (off is connected but disabled) and empty (linux only). * -G Display/Screen: Removed strict dependency on xdpyinfo to show advanced xorg screen and display data. Now it will show most of the data if xrandr is available, and all if xrandr and xdpyinfo are installed. More granular error messages as well. * -G Wayland Display: new type, d-rect: for > 1 monitor Wayland display layouts. Works roughly the same as Screen: s-res: does, except since Wayland has no 'Screen' concept, that goes into Display. This is sort of a rough algo, basically it takes either the dimensions of the total of x and y resolutions, or the greatest x or y resolution found for any monitor, whichever is greater, and uses that to create the display rectangle resolution composite value. * -G Display, Monitors: Extended display tool options from just xrandr to swaymsg, wlr-xrandr, weston-info, wayland-info. Still nothing on kwin_wayland or gnome-shell and mutter data. *. -S, -G: compositors, full redo of list, now supported: asc awc cage cagebreak cardboard chameleonwm clayland comfc dwc dwl epd-wm fireplace feathers fenestra glass gamescope greenfield grefson hikari hopalong inaban japokwm kiwmi kwinft labwc laikawm lipstick liri mahogany marina maze motorcar newm nucleus orbital perceptia phoc pywm qtile river rustland simulavr skylight sommelier sway swc swvkc tabby taiwins tinybox tinywl trinkster velox vimway vivarium wavy waybox way-cooler wayfire wayhouse waymonad westeros westford weston wio+ wio wxrc wxrd xuake * -G Enhanced Interfaces/GL item, previously only type OpenGL forX, now has: * X - OpenGL, requires glxinfo , same as before. * Wayland - EGL, currently no tool available, stub in place. Allegedly this data can be found but have no idea how or if a tool does that yet * Xvesa - Interface: interface type (VBE/GOP). GOP not confirmed, no data samples; v:, source:, dac: (no idea what it is, show it though), controller:, and ram: items. This is based on TinyX/Xvesa as found in TinyCore, but should work in Slitaz and Puppy TinyX as well if those projects are still around. * -G Display/Screen/Monitor data: Created structures and abstractions that allow for Wayland/Xorg/Xvesa data, most new features will work with any of these. Or Arcan, if that actually makes it, and we get data for it. We'll wait on Arcan, heh. * -G Display server: For Xvesa, added type TinyX to server if detected. Added Xwayland, which was not handled previously. For Xwayland, if wayland running, and if Xorg also installed, shows: server: X.org v: 1.20.14 with: Xwayland v: 21.01 Otherwise shows: server: Xwayland v: 21.01 * -G Compositors: fixed a long standing weak spot, if > 1 compositor detected running, not common, but could happen, shows all detected compositors. Display: x11 server: X.Org v: 1.20.13 compositors: 1: Mutter v: 41.1 2: xfwm v: 4.16.1 driver: X: loaded: modesetting gpu: radeon * -G drivers: now shows if X or gpu driver, in each its own section. This makes it more obvious what is going on: Display: x11 server: X.Org v: 1.20.13 driver: X: loaded: modesetting gpu: radeon resolution: * -Gxx Monitors: Show primary monitor with pos: primary,right. Uses either xrandr 'primary' value, or if no 'primary' found in an Xorg Screen, uses +0+0 positioned monitor. Position is based on the row and column number in the rectangular grid of monitors when monitors per Xorg Screen are > 1. For most common multi-monitor layouts, text positions are used, which are in general more clear and easy to understand than their internal numeric counterparts, that is, unless the layout is too complicated, it will show left, or top-left, instead of 1-1, and so on. Text mode positions are available for the following grid styles currently: 1x2, 1x3, 1x4, 2x1, 2x2, 2x3, 3x1, 3x2, 3x3. 'top' means the top row if > 1 row, 'bottom' means the bottom row, 'middle' is the middle row if 3 rows, 'left' is the first column, 'right' the last, 'center' if 3 columns, and 'center-l' (1-2), 'center-r' (1-3) are the 2 center columns if 4 columns. 'bottom-l', 'bottom-c', 'bottom-r'; 'middle-l', 'middle-c', 'middle-r'; 'top-left', 'top-center', 'top-right' complete the possible values. If the grid of monitors is greater than the supported rows or columns, it will switch to numeric row-column mode, with column-row numbering starting at 1-1, top left. * -Gxx Monitors: show (if detected, Linux only) monitor model, and if the display ID (from Xorg or Wayland) is different from the /sys monitor ID, show mapped: to show the /sys id. * -Gxxx Monitors: show modes: max: XxY min: XxY, or mode: XxY (if only 1 mode found). Shows hz: * -Ga Monitors: shows serial, built year, gamma, ratio, if detected. 2. Added impish 21-10 and jammy 22-04 to ubuntu id. That's for Mint base ID. Not huge point in updating if Mint doesn't update inxi, but there it is. 3. For -Axx, -Exx, -Gxx, -Nxx, shows PCIe speed and lanes. With -a, shows max speed / lanes if different than current speeds/lanes. Note that for unknown reasons not all devices in a PCIe slot show this data. 4. -Ixx: terminals added: foot, ate 5. -Sxx: login/display managers added: emptty, greetd, qingy, tbsm. See CODE 5 for more info on how this change was done. 6. -Sxxx: status/dock/panel bars added: i3-status-rs, luastatus, nwg-bar, nwg-dock, nwg-panel, rootbar, sfwbar, wapanel, waybar, yambar 7. Added a Tyan board IPMI sensor data set. 8. Added support for fruid_print for Elbrus -M Machine data. Those boards don't have dmi tables, but do ship with Elbrus OS which has fruid_print. 9. More disk vendors! Yes, you know the drill, the world turns, and with every turn, a flock of new vendors appears, like baby rabbits emerging from their warren, endlessly, a stream that is the life essence itself... or something. -------------------------------------------------------------------------------- CHANGES: 1. When xdpyinfo is not installed, user will still see advanced -Ga Monitor and Screen data as long as xrandr at least is installed. Better error messages as well now to explain which tool or tools missing caused the missing data. 2. -Gxx will show basic Screen and Monitors, id, mapped, pos:, model, res, dpi, diag; -Gxxx adds Monitor modes; --Ga adds screen/monitor size, Screen diag. 3. -ba/-v2 no longer show the full screens/monitor report, now it remains basic mode output, which it should have always done, unless -G is also explicitly added. 4. Split apart x-server version to v:, which should always have been the case. 5. Xvesa and Wayland no longer show glxinfo messages for no glxinfo for GL data. Now they show their own custom messages, appropriate to the case. 6. json features now test for JSON::PP, JSON::XS, or Cpanel::JSON::XS modules, and use whichever is found. Note I did not realize JSON::PP was in core modules as of 5.14 so that makes sense to use, and will allow inxi to start using json data sources, which are a lot easier to parse. 7. Changed -G drivers to show subsections for X and gpu drivers, and updated missing driver messages to account for this change. X drivers now show the sub sets of loaded/unloaded/failed/alternate, and gpu shows active gpu drivers, assuming such are detected. -------------------------------------------------------------------------------- DOCUMENTATION: 1. Help and man page updates for -G Display/Screen/Monitor changes. Redid -G, -Gx, -Gxx, -Gxxx, -Ga. Added monitor layout position feature. 2. Updated -Ga for xrandr/xdpyinfo changes. 3. Updated --recommends to more accurately show function of xdpyinfo and xrandr for -G and -Ga. 4. Reorganized and added complete table of contents to docs/inxi-data.txt -------------------------------------------------------------------------------- CODE: 1. Slightly optimized use of array loads on disk_vendor() and ram_vendor() based on how it's now done for monitor layouts, which is more efficient, use a scalar to hold a reference to the array, that avoids having the array ever exist in more than 1 place. Part of the ongoing process of avoiding extra hash and array copies globally. 2. Moved to consistent undef behaviors. * For lists of variables use () to undefine, changed all of the the following: 1. (@a,$b,$c,%d) = (undef,undef,undef,undef); 2. (@a,$b,$c,%d) = (undef); 3. (@a,$b,$c,%d) = undef; to use: (@a,$b,$c,%d) = (); This undefines all the variables in the list. Note that assigning undef to @a in the first example creates an array of 1 key, with the value undef, and (@a,@b) = (undef,undef) creates arrays of 2 indexes, or something like that. Not what was wanted. Examples 2 and 3 assign undef to @a: an array of 1 index, value undef, and undefine the others variables in the list. This was not the desired behavior! * For most scalars, arrays, and hashes, use: undef @a; undef $s; undef %h. * For some hash and array index values, use $h{a} = undef. These cases may want the key itself to exist, with the value of undef, though I believe: undef $h{a}; is synonymous, but still have to verify that. I did some testing, and realized that some of the undef I had used in the various previous ways of using undef were not actually resulting in the expected behaviors. 3. Refactored display_data_x into 3 functions, added display_data_xdpyinfo and display_data_xrandr, which allows for more granular handling of those dependencies, now inxi can show most advanced display data with only xrandr installed. 4. Significantly improved all error handling and missing data for Wayland/Xorg. 5. Refactored get_display_manager() to better handle corner case dm file or directory names, and to avoid endless loops. Much cleaner now. Required because greetd had varying file names, greetd.run, or just greet-546.sock. With some other dm's that use similar, or unreadable directories in /run, now just doing a glob of /run/ /var/run, /var/run/rc.d as detected and checking for the dms in the names, then just using the dms that were found. Simpler. 6. Massively simplified and integrated compositor logic in Graphics, now using program_values() and program_data() as appropriate, and simple matching list to ps_gui data to get detected compositor[s], much simpler, far more efficient code, less to maintain. Also fixed long-standing weak spot of exiting on first detected compositor, now shows all detected, with version etc for each if available. 7. With 6. also significantly simplified and optimized get_ps_de_data() for desktop data, that's the ps aux fallback case for wm desktop detections. 8. Made $wl compositors list global to avoid having to update each section, that's now used in -G compositor, -S desktop/wm, and wm sections. It is set in ps_gui() on initial load. 7. Settled on one and only way to do multiline conditionals, now use no space, use same indent level as starting if/elsif etc. I've been debating this one, but can't find any real way to handle that elegantly so I think best to just not try, and leave it up the code flow to show when it's wrapped condition tests. 8. Refactored previous gl_output, expanded it to handle all interface types, OpenGL, EGL (not currently active due to no known tool to get EGL data for Wayland, and Interface: VBE type data for Xvesa. This roughly completed the breaking apart of the X.org centric logic for Display, Monitors, and GL data, and make all sections now fully agnostic to display server or protocol type. Should new display servers appear, it will now be far more simple to add support for them, since they would just plug into the existing abstraction layers. 9. Added --debug-arg to allow for passing specific custom args to the debugger. 10. Refactored display_server version, now works much better, creates lists of server/version, and xwayland as well if found. --- inxi | 4139 ++++++++++++++++++++++++++++++++++++------------ inxi.1 | 527 +++--- inxi.changelog | 364 +++++ 3 files changed, 3765 insertions(+), 1265 deletions(-) diff --git a/inxi b/inxi index 41eadb3..4b2d538 100755 --- a/inxi +++ b/inxi @@ -2,6 +2,7 @@ ## infobash: Copyright (C) 2005-2007 Michiel de Boer aka locsmif ## inxi: Copyright (C) 2008-2021 Harald Hope ## Additional features (C) Scott Rogers - kde, cpu info +## Parse::EDID (C): 2005-2010 by Mandriva SA, Pascal Rigaux, Anssi Hannula ## Further fixes (listed as known): Horst Tritremmel ## Steven Barrett (aka: damentz) - usb audio patch; swap percent used patch ## Jarett.Stevens - dmidecode -M patch for older systems without /sys machine @@ -47,8 +48,8 @@ use POSIX qw(ceil uname strftime ttyname); ## INXI INFO ## my $self_name='inxi'; -my $self_version='3.3.12'; -my $self_date='2022-01-18'; +my $self_version='3.3.132'; +my $self_date='2022-02-22'; my $self_patch='00'; ## END INXI INFO ## my ($b_pledge,@pledges); @@ -103,7 +104,8 @@ if (eval {require Time::HiRes}){ my ($b_admin,$b_android,$b_busybox_ps,$b_cygwin,$b_display,$b_irc,$b_root); ## System -my ($bsd_type,$device_vm,$language,$os,$pci_tool,$wan_url) = ('','','','','',''); +my ($bsd_type,$device_vm,$language,$os,$pci_tool) = ('','','','',''); +my ($wan_url,$wl_compositors) = ('',''); my ($bits_sys,$cpu_arch,$ppid); my ($cpu_sleep,$dl_timeout,$limit,$ps_cols,$ps_count) = (0.35,4,10,0,5); my $sensors_cpu_nu = 0; @@ -514,6 +516,8 @@ sub set_os { } elsif ($cpu_arch =~ /(alpha|64|e2k|sparc_v9|sun4[uv]|ultrasparc)/){ $bits_sys = 64; + # force to string e2k, and also in case we need that ID changed + $cpu_arch = 'elbrus' if $cpu_arch =~ /e2k|elbrus/; } $b_cygwin = 1 if $os =~ /cygwin/; $b_android = 1 if -e '/system/build.prop'; @@ -1845,7 +1849,7 @@ sub display_data { push(@files, $system_files{'xorg-log'}) if $system_files{'xorg-log'}; push(@files, '/etc/X11/xorg.conf'); copy_files(\@files,'display-xorg'); - print "Collecting X, xprop, glxinfo, xrandr, xdpyinfo data, wayland, weston...\n"; + print "Collecting X, xprop, glxinfo, xrandr, xdpyinfo data, Wayland info...\n"; %data = ( 'desktop-session' => $ENV{'DESKTOP_SESSION'}, 'gdmsession' => $ENV{'GDMSESSION'}, @@ -1892,21 +1896,34 @@ sub display_data { ['kwin_x11','--version'], # ['locate','/Xorg'], # for Xorg.wrap problem ['loginctl','--no-pager list-sessions'], + ['ls','/sys/class/drm'], ['nvidia-settings','-q screens'], ['nvidia-settings','-c :0.0 -q all'], ['nvidia-smi','-q'], ['nvidia-smi','-q -x'], ['plasmashell','--version'], + ['swaymsg','-t get_inputs -p'], + ['swaymsg','-t get_inputs -r'], + ['swaymsg','-t get_outputs -p'], + ['swaymsg','-t get_outputs -r'], + ['swaymsg','-t get_tree'], + ['swaymsg','-t get_workspaces -p'], + ['swaymsg','-t get_workspaces -r'], ['vainfo',''], ['vdpauinfo',''], ['vulkaninfo',''], + ['wayland-info',''], # not packaged as far as I know yet ['weston-info',''], ['wmctrl','-m'], ['weston','--version'], + ['wlr-randr',''], ['xdpyinfo',''], ['Xorg','-version'], ['xprop','-root'], ['xrandr',''], + ['Xvesa','-version'], + ['Xvesa','-listmodes'], + ['Xwayland','-version'], ); run_commands(\@cmds,'display'); } @@ -2143,13 +2160,14 @@ sub system_files { sub run_self { print "Creating $self_name output file now. This can take a few seconds...\n"; print "Starting $self_name from: $self_path\n"; + my $a = ($debugger{'arg'}) ? ' ' . $debugger{'arg'} : ''; my $i = ($option eq 'main-full')? ' -i' : ''; my $z = ($debugger{'filter'}) ? ' -z' : ''; my $w = ($debugger{'width'}) ? $debugger{'width'} : 120; - my $iz = "$i$z"; - $iz =~ s/[\s-]//g; - my $self_file = "$data_dir/$self_name-FERfJLrploudma$iz-slots-y$w.txt"; - my $cmd = "$self_path/$self_name -FERfJLrploudma$i$z --slots --debug 10 -y $w > $self_file 2>&1"; + my $aiz = "$i$z"; + $aiz =~ s/[\s-]//g; + my $self_file = "$data_dir/$self_name-FERfJLrploudma$aiz-slots-y$w.txt"; + my $cmd = "$self_path/$self_name -FERfJLrploudma$i$z$a --slots --debug 10 -y $w > $self_file 2>&1"; system($cmd); copy($log_file, "$data_dir") or main::error_handler('copy-failed', "$log_file", "$!"); system("$self_path/$self_name --recommends -y 120 > $data_dir/$self_name-recommends-120.txt 2>&1"); @@ -2229,7 +2247,7 @@ sub get_glob { else { $item = main::message('root-required'); } - $item = main::message('undefined') if ! defined $item; + $item = main::message('undefined') if !defined $item; push(@result,$_ . '::' . $item); } # print Data::Dumper::Dumper \@result; @@ -2899,9 +2917,9 @@ sub check_items { $info_os = 'info-bsd'; } else { - @data = qw(blockdev bt-adapter dig dmidecode doas fdisk file hciconfig - hddtemp ifconfig ip ipmitool ipmi-sensors lsblk lsusb lvs mdadm modinfo - runlevel sensors smartctl strings sudo tree upower uptime); + @data = qw(blockdev bt-adapter dig dmidecode doas fdisk file fruid_print + hciconfig hddtemp ifconfig ip ipmitool ipmi-sensors lsblk lsusb lvs + mdadm modinfo runlevel sensors smartctl strings sudo tree upower uptime); } $b_program = 1; $item = 'Program'; @@ -2939,16 +2957,20 @@ sub check_items { } elsif ($type eq 'recommended Perl modules'){ @data = qw(File::Copy File::Find File::Spec::Functions HTTP::Tiny IO::Socket::SSL - Time::HiRes Cpanel::JSON::XS JSON::XS XML::Dumper Net::FTP); - push(@data, qw(OpenBSD::Pledge OpenBSD::Unveil)) if $bsd_type && $bsd_type eq 'openbsd'; + Time::HiRes JSON::PP Cpanel::JSON::XS JSON::XS XML::Dumper Net::FTP); + if ($bsd_type && $bsd_type eq 'openbsd'){ + push(@data, qw(OpenBSD::Pledge OpenBSD::Unveil)); + } $b_perl_module = 1; $item = 'Perl Module'; $extra = ' (Optional)'; - $extra2 = "None of these are strictly required, but if you have them all, you can - eliminate some recommended non Perl programs from the install. "; - $extra3 = "HTTP::Tiny and IO::Socket::SSL must both be present to use as a downloader option. - For json export Cpanel::JSON::XS is preferred over JSON::XS. To run --debug 20-22 File::Copy, - File::Find, and File::Spec::Functions must be present (most distros have these in Core Modules). + $extra2 = "None of these are strictly required, but if you have them all, + you can eliminate some recommended non Perl programs from the install. "; + $extra3 = "HTTP::Tiny and IO::Socket::SSL must both be present to use as a + downloader option. For json export Cpanel::JSON::XS is preferred over + JSON::XS, but JSON::PP is in core modules. To run --debug 20-22 File::Copy, + File::Find, and File::Spec::Functions must be present (most distros have + these in Core Modules). "; } elsif ($type eq 'recommended kernel modules'){ @@ -3006,9 +3028,9 @@ sub check_items { %info = item_data($item); $about = $info{$info_os}; if (($b_dir && -d $item) || ($b_file && -r $item) || - ($b_program && main::check_program($item)) || - ($b_perl_module && main::check_perl_module($item)) || - ($b_kernel_module && @modules && (grep {/^$item$/} @modules))){ + ($b_program && main::check_program($item)) || + ($b_perl_module && main::check_perl_module($item)) || + ($b_kernel_module && @modules && (grep {/^$item$/} @modules))){ $result = 'Present'; } elsif ($b_file && -f $item){ @@ -3210,6 +3232,13 @@ sub item_data { 'pacman' => '', 'rpm' => '', }, + 'fruid_print' => { + 'info' => '-M machine data, Elbrus only', + 'info-bsd' => '', + 'apt' => '', + 'pacman' => '', + 'rpm' => '', + }, 'glabel' => { 'info' => '', 'info-bsd' => '-R; -D; -P. Get actual gptid /dev path', @@ -3408,8 +3437,8 @@ sub item_data { }, # Display Tools 'glxinfo' => { - 'info' => '-G glx info', - 'info-bsd' => '-G glx info', + 'info' => '-G (X) glx info', + 'info-bsd' => '-G (X) glx info', 'apt' => 'mesa-utils', 'pacman' => 'mesa-demos', 'rpm' => 'glx-utils (SUSE: Mesa-demo-x)', @@ -3422,30 +3451,30 @@ sub item_data { 'rpm' => 'wmctrl', }, 'xdpyinfo' => { - 'info' => '-G multi screen resolution', - 'info-bsd' => '-G multi screen resolution', + 'info' => '-G (X) Screen resolution, dpi; -Ga Screen size', + 'info-bsd' => '-G (X) Screen resolution, dpi; -Ga Screen size', 'apt' => 'X11-utils', 'pacman' => 'xorg-xdpyinfo', 'rpm' => 'xorg-x11-utils (SUSE/Fedora?: xdpyinfo)', }, 'xprop' => { - 'info' => '-S desktop data', - 'info-bsd' => '-S desktop data', + 'info' => '-S (X) desktop data', + 'info-bsd' => '-S (X) desktop data', 'apt' => 'X11-utils', 'pacman' => 'xorg-xprop', 'rpm' => 'x11-utils', }, 'xrandr' => { - 'info' => '-G single screen resolution', - 'info-bsd' => '-G single screen resolution', + 'info' => '-G (X) monitors(s) resolution; -Ga monitor data', + 'info-bsd' => '-G (X) monitors(s) resolution; -Ga monitor data', 'apt' => 'x11-xserver-utils', 'pacman' => 'xrandr', 'rpm' => 'x11-server-utils (Fedora: xrandr)', }, # Perl Modules 'Cpanel::JSON::XS' => { - 'info' => '--output json - required for export.', - 'info-bsd' => '--output json - required for export.', + 'info' => '--output json (faster than JSON::PP).', + 'info-bsd' => '--output json (faster than JSON::PP).', 'apt' => 'libcpanel-json-xs-perl', 'pacman' => 'perl-cpanel-json-xs', 'rpm' => 'perl-Cpanel-JSON-XS', @@ -3485,9 +3514,16 @@ sub item_data { 'pacman' => 'perl-io-socket-ssl', 'rpm' => 'perl-IO-Socket-SSL', }, + 'JSON::PP' => { + 'info' => '--output json (in CoreModules, but slower).', + 'info-bsd' => '--output json (in CoreModules, but slower).', + 'apt' => 'libcpanel-json-xs-perl', + 'pacman' => 'perl-cpanel-json-xs', + 'rpm' => 'perl-Cpanel-JSON-XS', + }, 'JSON::XS' => { - 'info' => '--output json - required for export (legacy).', - 'info-bsd' => '--output json - required for export (legacy).', + 'info' => '--output json (legacy).', + 'info-bsd' => '--output json (legacy).', 'apt' => 'libjson-xs-perl', 'pacman' => 'perl-json-xs', 'rpm' => 'perl-JSON-XS', @@ -3751,6 +3787,34 @@ sub lister { closedir $dir; return @list; } +# checks for 1 of 3 perl json modules. All three have same encode_json, +# decode_json() methods. +sub load_json { + eval $start if $b_log; + $loaded{'json'} = 1; + # recommended, but not in core modules + if (check_perl_module('Cpanel::JSON::XS')){ + Cpanel::JSON::XS->import; + $use{'json'} = {'type' => 'cpanel-json-xs', + 'encode' => \&Cpanel::JSON::XS::encode_json, + 'decode' => \&Cpanel::JSON::XS::decode_json}; + } + # somewhat legacy, not in perl modules + elsif (check_perl_module('JSON::XS')){ + JSON::XS->import; + $use{'json'} = {'type' => 'json-xs', + 'encode' => \&JSON::XS::encode_json, + 'decode' => \&JSON::XS::decode_json}; + } + # perl, in core modules as of 5.14 + elsif (check_perl_module('JSON::PP')){ + JSON::PP->import; + $use{'json'} = {'type' => 'json-pp', + 'encode' => \&JSON::PP::encode_json, + 'decode' => \&JSON::PP::decode_json}; + } + eval $end if $b_log; +} # returns array of: 0: program print name 1: program version # args: 1: program values id 2: program version string @@ -3813,17 +3877,22 @@ sub set_program_values { 'amiwm' => ['^amiwm',0,'0','AmiWM',0,1,0,'',''], # no version 'antiwm' => ['^antiwm',0,'0','AntiWM',0,1,0,'',''], # no version known 'asc' => ['^asc',0,'0','asc',0,1,0,'',''], + 'awc' => ['^awc',0,'0','awc',0,1,0,'',''], # unverified 'awesome' => ['^awesome',2,'--version','awesome',0,1,0,'',''], 'beryl' => ['^beryl',0,'0','Beryl',0,1,0,'',''], # unverified; legacy 'blackbox' => ['^Blackbox',2,'--version','Blackbox',0,1,0,'',''], 'bspwm' => ['^\S',1,'-v','bspwm',0,1,0,'',''], 'budgie-desktop' => ['^budgie-desktop',2,'--version','Budgie',0,1,0,'',''], 'budgie-wm' => ['^budgie',0,'0','budgie-wm',0,1,0,'',''], + 'cage' => ['^cage',0,'0','Cage',0,1,0,'',''], # unverified 'cagebreak' => ['^Cagebreak',3,'-v','Cagebreak',0,1,0,'',''], 'calmwm' => ['^calmwm',0,'0','CalmWM',0,1,0,'',''], # unverified + 'cardboard' => ['^cardboard',0,'0','Cardboard',0,1,0,'',''], # unverified 'catwm' => ['^catwm',0,'0','catwm',0,1,0,'',''], # unverified + 'chameleonwm' => ['^chameleon',0,'0','ChameleonWM',0,1,0,'',''], # unverified 'cinnamon' => ['^cinnamon',2,'--version','Cinnamon',0,1,0,'',''], 'clfswm' => ['^clsfwm',0,'0','clfswm',0,1,0,'',''], # no version + 'comfc' => ['^comfc',0,'0','comfc',0,1,0,'',''], # unverified 'compiz' => ['^compiz',2,'--version','Compiz',0,1,0,'',''], 'compton' => ['^\d',1,'--version','Compton',0,1,0,'',''], 'ctwm' => ['^\S',1,'-version','ctwm',0,1,0,'',''], @@ -3834,11 +3903,15 @@ sub set_program_values { 'deepin-mutter' => ['^mutter',2,'--version','Deepin-Mutter',0,1,0,'',''], 'deepin-wm' => ['^gala',0,'0','DeepinWM',0,1,0,'',''], # no version 'dwc' => ['^dwc',0,'0','dwc',0,1,0,'',''], # unverified + 'dwl' => ['^dwl',0,'0','dwl',0,1,0,'',''], # unverified 'dwm' => ['^dwm',1,'-v','dwm',0,1,1,'^dwm-',''], 'echinus' => ['^echinus',1,'-v','echinus',0,1,1,'',''], # echinus-0.4.9 (c)... # only listed here for compositor values, version data comes from xprop 'enlightenment' => ['^enlightenment',0,'0','enlightenment',0,1,0,'',''], # no version, yet? + 'epd-wm' => ['^epd-wm',0,'0','epd-wm',0,1,0,'',''], # unverified 'evilwm' => ['evilwm',3,'-V','evilwm',0,1,0,'',''],# might use full path in match + 'feathers' => ['^feathers',0,'0','feathers',0,1,0,'',''], # unverified + 'fenestra' => ['^fenestra',0,'0','fenestra',0,1,0,'',''], # unverified 'fireplace' => ['^fireplace',0,'0','fireplace',0,1,0,'',''], # unverified 'fluxbox' => ['^fluxbox',2,'-v','Fluxbox',0,1,0,'',''], 'flwm' => ['^flwm',0,'0','FLWM',0,0,1,'',''], # no version @@ -3850,18 +3923,24 @@ sub set_program_values { 'fvwm95' => ['^fvwm',2,'--version','FVWM95',0,1,1,'',''], 'fvwm-crystal' => ['^fvwm',2,'--version','FVWM-Crystal',0,0,0,'',''], # for print name fvwm 'gala' => ['^gala',0,'0','gala',0,1,0,'',''], # pantheon wm: super slow result, 2, '--version' works? + 'gamescope' => ['^gamescope',0,'0','Gamescope',0,1,0,'',''], # unverified 'glass' => ['^glass',3,'-v','Glass',0,1,0,'',''], 'gnome' => ['^gnome',3,'--version','GNOME',0,1,0,'',''], # no version, print name 'gnome-about' => ['^gnome',3,'--version','GNOME',0,1,0,'',''], 'gnome-shell' => ['^gnome',3,'--version','gnome-shell',0,1,0,'',''], - 'grefson' => ['^grefson',0,'0','grefson',0,1,0,'',''], # unverified + 'greenfield' => ['^greenfield',0,'0','Greenfield',0,1,0,'',''], # unverified + 'grefson' => ['^grefson',0,'0','Grefson',0,1,0,'',''], # unverified 'hackedbox' => ['^hackedbox',2,'-version','HackedBox',0,1,0,'',''], # unverified, assume blackbox # note, herbstluftwm when launched with full path returns full path in version string 'herbstluftwm' => ['herbstluftwm',2,'--version','herbstluftwm',0,1,0,'',''], + 'hikari' => ['^hikari',0,'0','hikari',0,1,0,'',''], # unverified + 'hopalong' => ['^hopalong',0,'0','Hopalong',0,1,0,'',''], # unverified 'i3' => ['^i3',3,'--version','i3',0,1,0,'',''], 'icewm' => ['^icewm',2,'--version','IceWM',0,1,0,'',''], + 'inaban' => ['^inaban',0,'0','inaban',0,1,0,'',''], # unverified 'instantwm' => ['^instantwm',1,'-v','instantWM',0,1,1,'^instantwm-?(instantos-?)?',''], 'ion3' => ['^ion3',0,'--version','Ion3',0,1,0,'',''], # unverified; also shell called ion + 'japokwm' => ['^japokwm',0,'0','japokwm',0,1,0,'',''], # unverified 'jbwm' => ['jbwm',3,'-v','JBWM',0,1,0,'',''], # might use full path in match 'jwm' => ['^jwm',2,'--version','JWM',0,1,0,'',''], 'kded' => ['^KDE( Development Platform)?:',2,'--version','KDE',0,1,0,'\sDevelopment Platform',''], @@ -3869,13 +3948,19 @@ sub set_program_values { 'kded2' => ['^KDE( Development Platform)?:',2,'--version','KDE',0,1,0,'\sDevelopment Platform',''], 'kded3' => ['^KDE( Development Platform)?:',2,'--version','KDE',0,1,0,'\sDevelopment Platform',''], 'kded4' => ['^KDE( Development Platform)?:',2,'--version','KDE',0,1,0,'\sDevelopment Platform',''], + 'kiwmi' => ['^kwimi',0,'0','kiwmi',0,1,0,'',''], # unverified 'ksmcon' => ['^ksmcon',0,'0','ksmcon',0,1,0,'',''],# no version 'kwin' => ['^kwin',0,'0','kwin',0,1,0,'',''],# no version 'kwin_wayland' => ['^kwin_wayland',0,'0','kwin_wayland',0,1,0,'',''],# no version 'kwin_x11' => ['^kwin_x11',0,'0','kwin_x11',0,1,0,'',''],# no version + 'kwinft' => ['^kwinft',0,'0','KWinFT',0,1,0,'',''], # unverified + 'labwc' => ['^labwc',0,'0','LabWC',0,1,0,'',''], # unverified + 'laikawm' => ['^laikawm',0,'0','LaikaWM',0,1,0,'',''], # unverified 'larswm' => ['^larswm',2,'-v','larswm',0,1,1,'',''], 'leftwm' => ['^leftwm',0,'0','LeftWM',0,1,0,'',''],# no version, in CHANGELOG 'liri' => ['^liri',0,'0','liri',0,1,0,'',''], + 'lipstick' => ['^lipstick',0,'0','Lipstick',0,1,0,'',''], # unverified + 'liri' => ['^liri',0,'0','liri',0,1,0,'',''], # unverified 'lumina-desktop' => ['^\S',1,'--version','Lumina',0,1,1,'',''], 'lwm' => ['^lwm',0,'0','lwm',0,1,0,'',''], # no version 'lxpanel' => ['^lxpanel',2,'--version','LXDE',0,1,0,'',''], @@ -3883,13 +3968,16 @@ sub set_program_values { 'lxqt-panel' => ['^lxqt-panel',2,'--version','LXQt',0,1,0,'',''], 'lxqt-variant' => ['^lxqt-panel',0,'0','LXQt-Variant',0,1,0,'',''], 'lxsession' => ['^lxsession',0,'0','lxsession',0,1,0,'',''], + 'mahogany' => ['^mahogany',0,'0','Mahogany',0,1,0,'',''], # unverified 'manokwari' => ['^manokwari',0,'0','Manokwari',0,1,0,'',''], + 'marina' => ['^marina',0,'0','Marina',0,1,0,'',''], # unverified 'marco' => ['^marco',2,'--version','marco',0,1,0,'',''], 'matchbox' => ['^matchbox',0,'0','Matchbox',0,1,0,'',''], 'matchbox-window-manager' => ['^matchbox',2,'--help','Matchbox',0,0,0,'',''], 'mate-about' => ['^MATE[[:space:]]DESKTOP',-1,'--version','MATE',0,1,0,'',''], # note, mate-session when launched with full path returns full path in version string 'mate-session' => ['mate-session',-1,'--version','MATE',0,1,0,'',''], + 'maze' => ['^maze',0,'0','Maze',0,1,0,'',''], # unverified 'mcwm' => ['^mcwm',0,'0','mcwm',0,1,0,'',''], # unverified/see 2bwm 'metacity' => ['^metacity',2,'--version','Metacity',0,1,0,'',''], 'metisse' => ['^metisse',0,'0','metisse',0,1,0,'',''], @@ -3903,23 +3991,31 @@ sub set_program_values { 'mutter' => ['^mutter',2,'--version','Mutter',0,1,0,'',''], 'mwm' => ['^mwm',0,'0','MWM',0,1,0,'',''],# no version 'nawm' => ['^nawm',0,'0','nawm',0,1,0,'',''],# unverified + 'newm' => ['^newm',0,'0','newm',0,1,0,'',''], # unverified 'notion' => ['^.',1,'--version','Notion',0,1,0,'',''], + 'nucleus' => ['^nucleus',0,'0','Nucleus',0,1,0,'',''], # unverified 'openbox' => ['^openbox',2,'--version','Openbox',0,1,0,'',''], - 'orbital' => ['^orbital',0,'0','orbital',0,1,0,'',''],# unverified + 'orbital' => ['^orbital',0,'0','Orbital',0,1,0,'',''],# unverified 'pantheon' => ['^pantheon',0,'0','Pantheon',0,1,0,'',''],# no version 'papyros' => ['^papyros',0,'0','papyros',0,1,0,'',''],# no version 'pekwm' => ['^pekwm',3,'--version','PekWM',0,1,0,'',''], 'penrose' => ['^penrose',0,'0','Penrose',0,1,0,'',''],# no version? 'perceptia' => ['^perceptia',0,'0','perceptia',0,1,0,'',''], + 'phoc' => ['^phoc',0,'0','phoc',0,1,0,'',''], # unverified 'picom' => ['^\S',1,'--version','Picom',0,1,0,'^v',''], 'plasmashell' => ['^plasmashell',2,'--version','KDE Plasma',0,1,0,'',''], + 'pywm' => ['^pywm',0,'0','pywm',0,1,0,'',''], # unverified 'qtile' => ['^',1,'--version','Qtile',0,1,0,'',''], 'qvwm' => ['^qvwm',0,'0','qvwm',0,1,0,'',''], # unverified 'razor-session' => ['^razor',0,'0','Razor-Qt',0,1,0,'',''], 'ratpoison' => ['^ratpoison',2,'--version','Ratpoison',0,1,0,'',''], + 'river' => ['^river',0,'0','River',0,1,0,'',''], # unverified + 'rootston' => ['^rootston',0,'0','rootston',0,1,0,'',''], # unverified, wlroot ref 'rustland' => ['^rustland',0,'0','rustland',0,1,0,'',''], # unverified 'sawfish' => ['^sawfish',3,'--version','Sawfish',0,1,0,'',''], 'scrotwm' => ['^scrotwm.*welcome.*',5,'-v','scrotwm',0,1,1,'',''], + 'simulavr' => ['simulavr^',0,'0','SimulaVR',0,1,0,'',''], # unverified + 'skylight' => ['^skylight',0,'0','Skylight',0,1,0,'',''], # unverified 'sommelier' => ['^sommelier',0,'0','sommelier',0,1,0,'',''], # unverified 'snapwm' => ['^snapwm',0,'0','snapwm',0,1,0,'',''], # unverified 'spectrwm' => ['^spectrwm.*welcome.*wm',5,'-v','spectrwm',0,1,1,'',''], @@ -3927,7 +4023,13 @@ sub set_program_values { 'stumpwm' => ['^SBCL',0,'--version','StumpWM',0,1,0,'',''], # hangs when run in wm 'sway' => ['^sway',3,'-v','sway',0,1,0,'',''], 'swc' => ['^swc',0,'0','swc',0,1,0,'',''], # unverified + 'swvkc' => ['^swvkc',0,'0','swvkc',0,1,0,'',''], # unverified + 'tabby' => ['^tabby',0,'0','Tabby',0,1,0,'',''], # unverified + 'taiwins' => ['^taiwins',0,'0','taiwins',0,1,0,'',''], # unverified + 'tinybox' => ['^tinybox',0,'0','tinybox',0,1,0,'',''], # unverified + 'tinywl' => ['^tinywl',0,'0','TinyWL',0,1,0,'',''], # unverified 'tinywm' => ['^tinywm',0,'0','TinyWM',0,1,0,'',''], # no version + 'trinkster' => ['^trinkster',0,'0','Trinkster',0,1,0,'',''], # unverified 'tvtwm' => ['^tvtwm',0,'0','tvtwm',0,1,0,'',''], # unverified 'twin' => ['^Twin:',2,'--version','Twin',0,0,0,'',''], 'twm' => ['^twm',0,'0','TWM',0,1,0,'',''], # no version @@ -3938,15 +4040,23 @@ sub set_program_values { 'unity-system-compositor' => ['^unity-system-compositor',2,'--version', 'unity-system-compositor (mir)',0,0,0,'',''], 'uwm' => ['^uwm',0,'0','UWM',0,1,0,'',''], # unverified + 'velox' => ['^velox',0,'0','Velox',0,1,0,'',''], # unverified + 'vimway' => ['^vimway',0,'0','vimway',0,1,0,'',''], # unverified + 'vivarium' => ['^vivarium',0,'0','Vivarium',0,1,0,'',''], # unverified 'wavy' => ['^wavy',0,'0','wavy',0,1,0,'',''], # unverified + 'waybox' => ['^way',0,'0','waybox',0,1,0,'',''], # unverified 'waycooler' => ['^way',3,'--version','way-cooler',0,1,0,'',''], 'way-cooler' => ['^way',3,'--version','way-cooler',0,1,0,'',''], - 'wayfire' => ['^way',0,'0','wayfire',0,1,0,'',''], # unverified + 'wayfire' => ['^\d',1,'--version','wayfire',0,1,0,'',''], # -version/--version 'wayhouse' => ['^wayhouse',0,'0','wayhouse',0,1,0,'',''], # unverified + 'waymonad' => ['^waymonad',0,'0','waymonad',0,1,0,'',''], # unverified + 'westeros' => ['^westeros',0,'0','westeros',0,1,0,'',''], # unverified 'westford' => ['^westford',0,'0','westford',0,1,0,'',''], # unverified - 'weston' => ['^weston',0,'0','weston',0,1,0,'',''], # unverified + 'weston' => ['^weston',0,'0','Weston',0,1,0,'',''], # unverified 'windowlab' => ['^windowlab',2,'-about','WindowLab',0,1,0,'',''], 'wingo' => ['^wingo',0,'0','Wingo',0,1,0,'',''], # unverified + 'wio' => ['^wio',0,'0','Wio',0,1,0,'',''], # unverified + 'wio' => ['^wio\+',0,'0','wio+',0,1,0,'',''], # unverified 'wm2' => ['^wm2',0,'0','wm2',0,1,0,'',''], # no version 'wmaker' => ['^Window[[:space:]]*Maker',-1,'--version','WindowMaker',0,1,0,'',''], 'wmfs' => ['^wmfs',0,'0','WMFS',0,1,0,'',''], # unverified @@ -3954,6 +4064,8 @@ sub set_program_values { 'wmii' => ['^wmii',1,'-v','wmii',0,1,0,'^wmii[234]?-',''], # wmii is wmii3 'wmii2' => ['^wmii2',1,'--version','wmii2',0,1,0,'^wmii[234]?-',''], 'wmx' => ['^wmx',0,'0','wmx',0,1,0,'',''], # no version + 'wxrc' => ['^wx',0,'0','',0,1,0,'WXRC',''], # unverified + 'wxrd' => ['^wx',0,'0','',0,1,0,'WXRD',''], # unverified 'xcompmgr' => ['^xcompmgr',0,'0','xcompmgr',0,1,0,'',''], # no version 'xfce4-panel' => ['^xfce4-panel',2,'--version','Xfce',0,1,0,'',''], 'xfce5-panel' => ['^xfce5-panel',2,'--version','Xfce',0,1,0,'',''], @@ -3965,6 +4077,7 @@ sub set_program_values { 'xfwm4' => ['xfwm4? version',5,'--version','xfwm',0,1,0,'^^\s+',''], 'xfwm5' => ['xfwm5? version',5,'--version','xfwm',0,1,0,'^^\s+',''], # unverified 'xmonad' => ['^xmonad',2,'--version','XMonad',0,1,0,'',''], + 'xuake' => ['^xuake',0,'0','xuake',0,1,0,'',''], # unverified 'yeahwm' => ['^yeahwm',0,'--version','YeahWM',0,1,0,'',''], # unverified ## Toolkits ## 'gtk-launch' => ['^\S',1,'--version','GTK',0,1,0,'',''], @@ -3972,9 +4085,11 @@ sub set_program_values { 'qtdiag' => ['^qt',2,'--version','Qt',0,1,0,'',''], ## Display Managers (dm) ## 'cdm' => ['^cdm',0,'0','CDM',0,1,0,'',''], + 'emptty' => ['^emptty',0,'0','EMPTTY',0,1,0,'',''], # unverified 'entrance' => ['^entrance',0,'0','Entrance',0,1,0,'',''], 'gdm' => ['^gdm',2,'--version','GDM',0,1,0,'',''], 'gdm3' => ['^gdm',2,'--version','GDM3',0,1,0,'',''], + 'greetd' => ['^greetd',0,'0','greetd',0,1,0,'',''], # no version 'kdm' => ['^kdm',0,'0','KDM',0,1,0,'',''], 'kdm3' => ['^kdm',0,'0','KDM',0,1,0,'',''], 'ldm' => ['^ldm',0,'0','LDM',0,1,0,'',''], @@ -3984,8 +4099,10 @@ sub set_program_values { 'mdm' => ['^mdm',0,'0','MDM',0,1,0,'',''], 'nodm' => ['^nodm',0,'0','nodm',0,1,0,'',''], 'pcdm' => ['^pcdm',0,'0','PCDM',0,1,0,'',''], + 'qingy' => ['^qingy',0,'0','qingy',0,1,0,'',''], # unverified 'sddm' => ['^sddm',0,'0','SDDM',0,1,0,'',''], 'slim' => ['slim version',3,'-v','SLiM',0,1,0,'',''], + 'tbsm' => ['^tbsm',0,'0','tbsm',0,1,0,'',''], # unverified 'tdm' => ['^tdm',0,'0','TDM',0,1,0,'',''], 'udm' => ['^udm',0,'0','udm',0,1,0,'',''], 'wdm' => ['^wdm',0,'0','WINGs DM',0,1,0,'',''], @@ -4521,7 +4638,7 @@ sub get { } else { main::error_handler('bad-arg', $opt, $arg); - } }, + }}, 'C|cpu' => sub { $show{'short'} = 0; $show{'cpu'} = 1;}, @@ -4549,6 +4666,7 @@ sub get { $show{'disk'} = 1; $show{'graphic'} = 1; $show{'graphic-basic'} = 1; + $show{'graphic-full'} = 1; $show{'info'} = 1; $show{'machine'} = 1; $show{'network'} = 1; @@ -4561,7 +4679,8 @@ sub get { 'G|graphics|graphic' => sub { $show{'short'} = 0; $show{'graphic'} = 1; - $show{'graphic-basic'} = 1;}, + $show{'graphic-basic'} = 1; + $show{'graphic-full'} = 1;}, 'h|help|?' => sub { $show{'help'} = 1;}, 'i|ip' => sub { @@ -4588,7 +4707,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'L|logical|lvm' => sub { $show{'short'} = 0; $show{'logical'} = 1;}, @@ -4630,7 +4749,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'r|repos|repo' => sub { $show{'short'} = 0; $show{'repo'} = 1;}, @@ -4649,7 +4768,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'slots|slot' => sub { $show{'short'} = 0; $show{'slot'} = 1;}, @@ -4674,7 +4793,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'u|uuid' => sub { $show{'uuid'} = 1;}, 'v|verbosity:i' => sub { @@ -4711,11 +4830,11 @@ sub get { if ($arg >= 5){ $show{'audio'} = 1; $show{'bluetooth'} = 1; - $show{'ram'} = 1; + $show{'graphic-full'} = 1; $show{'label'} = 1; $show{'optical-basic'} = 1; - $show{'ram'} = 1; $show{'raid'} = 1; + $show{'ram'} = 1; $show{'sensor'} = 1; $show{'swap'} = 1; $show{'uuid'} = 1; @@ -4750,7 +4869,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'V|version' => sub { $show{'version'} = 1 }, 'w|weather' => sub { @@ -4762,7 +4881,7 @@ sub get { } else { main::error_handler('distro-block', $opt); - } }, + }}, 'W|weather-location:s' => sub { my ($opt,$arg) = @_; $arg ||= ''; @@ -4780,7 +4899,7 @@ sub get { } else { main::error_handler('distro-block', $opt); - } }, + }}, 'ws|weather-source:s' => sub { my ($opt,$arg) = @_; # let api processor handle checks if valid, this @@ -4790,7 +4909,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'weather-unit:s' => sub { my ($opt,$arg) = @_; $arg ||= ''; @@ -4803,7 +4922,7 @@ sub get { } else { main::error_handler('bad-arg',$opt,$arg); - } }, + }}, 'x|extra:i' => sub { my ($opt,$arg) = @_; if ($arg > 0){ @@ -4811,7 +4930,7 @@ sub get { } else { $extra++; - } }, + }}, 'y|width:i' => sub { my ($opt, $arg) = @_; if (defined $arg && $arg == -1){ @@ -4908,7 +5027,7 @@ sub get { } }, 'cygwin' => sub { - $b_cygwin = 1 }, + $b_cygwin = 1;}, 'dbg:i' => sub { my ($opt,$arg) = @_; if ($arg > 0){ @@ -4924,7 +5043,15 @@ sub get { } else { main::error_handler('bad-arg', $opt, $arg); - } }, + }}, + 'debug-arg:s' => sub { + my ($opt,$arg) = @_; + if ($arg){ + $debugger{'arg'} = $arg; + } + else { + main::error_handler('bad-arg', $opt, $arg); + }}, 'debug-filter|debug-z|debug-zy' => sub { $debugger{'filter'} = 1 }, 'debug-id:s' => sub { @@ -4934,7 +5061,7 @@ sub get { } else { main::error_handler('bad-arg', $opt, $arg); - } }, + }}, 'debug-no-eps' => sub { $debugger{'no-exit'} = 1; $debugger{'no-proc'} = 1; @@ -4962,7 +5089,7 @@ sub get { } else { main::error_handler('bad-arg', $opt, $arg); - } }, + }}, 'dig' => sub { $force{'no-dig'} = 0;}, 'display:s' => sub { @@ -4977,9 +5104,9 @@ sub get { } else { main::error_handler('bad-arg', $opt, $arg); - } }, + }}, 'dmi|dmidecode' => sub { - $force{'dmidecode'} = 1 }, + $force{'dmidecode'} = 1;}, 'downloader:s' => sub { my ($opt,$arg) = @_; $arg = lc($arg); @@ -5001,14 +5128,14 @@ sub get { } else { main::error_handler('bad-arg', $opt, $arg); - } }, + }}, 'fake:s' => sub { my ($opt,$arg) = @_; if ($arg){ - my $wl = 'bluetooth|compiler|cpu|dboot|dmidecode|ipmi|logical|lspci|'; + my $wl = 'bluetooth|compiler|cpu|dboot|dmidecode|elbrus|ipmi|logical|lspci|'; $wl .= 'partitions|pciconf|pcictl|pcidump|raid-btrfs|raid-hw|raid-lvm|'; - $wl .= 'raid-md|raid-soft|raid-zfs|sensors|sysctl|uptime|usbconfig|'; - $wl .= 'usbdevs|vmstat|xorg-log'; + $wl .= 'raid-md|raid-soft|raid-zfs|sensors|swaymsg|sysctl|'; + $wl .= 'uptime|usbconfig|usbdevs|vmstat|wl-info|wlr-randr|xorg-log|xrandr'; for (split(',',$arg)){ if ($_ =~ /\b($wl)\b/){ $fake{lc($1)} = 1; @@ -5025,7 +5152,7 @@ sub get { my ($opt,$arg) = @_; if ($arg){ my $wl = 'colors|cpuinfo|display|dmidecode|hddtemp|lsusb|man|meminfo|'; - $wl .= 'no-dig|no-doas|no-html-wan|no-sudo|pkg|usb-sys|vmstat|wmctrl'; + $wl .= 'no-dig|no-doas|no-html-wan|no-sudo|pkg|usb-sys|vmstat|wayland|wmctrl'; for (split(',',$arg)){ if ($_ =~ /\b($wl)\b/){ $force{lc($1)} = 1; @@ -5048,10 +5175,10 @@ sub get { main::error_handler('bad-arg', $opt, $arg); }}, 'hddtemp' => sub { - $force{'hddtemp'} = 1 }, + $force{'hddtemp'} = 1;}, 'host|hostname' => sub { $show{'host'} = 1; - $show{'no-host'} = 0}, + $show{'no-host'} = 0;}, 'html-wan' => sub { $force{'no-html-wan'} = 0;}, 'indent:i' => sub { @@ -5099,14 +5226,14 @@ sub get { 'no-doas' => sub { $force{'no-doas'} = 1;}, 'no-host|no-hostname' => sub { - $show{'host'} = 0 ; - $show{'no-host'} = 1}, + $show{'host'} = 0; + $show{'no-host'} = 1;}, 'no-html-wan' => sub { $force{'no-html-wan'}= 1;}, 'no-man' => sub { $use{'no-man'} = 0;}, 'no-ssl' => sub { - $dl{'no-ssl-opt'}=1 }, + $dl{'no-ssl-opt'} = 1;}, 'no-sudo' => sub { $force{'no-sudo'} = 1;}, 'output-file:s' => sub { @@ -5123,7 +5250,7 @@ sub get { main::error_handler('bad-arg', $opt, $arg); }}, 'pkg' => sub { - $force{'pkg'} = 1 }, + $force{'pkg'} = 1;}, 'ppc' => sub { undef %risc; $risc{'id'} = 'ppc'; @@ -5164,9 +5291,9 @@ sub get { my ($opt,$arg) = @_; process_updater($opt,$arg);}, 'usb-sys' => sub { - $force{'usb-sys'} = 1 }, + $force{'usb-sys'} = 1;}, 'usb-tool' => sub { - $force{'lsusb'} = 1 }, + $force{'lsusb'} = 1;}, 'wan-ip-url:s' => sub { my ($opt,$arg) = @_; if ($arg && $arg =~ /^(f|ht)tp[s]?:\/\//){ @@ -5176,8 +5303,10 @@ sub get { else { main::error_handler('bad-arg', $opt, $arg); }}, - 'wm' => sub { - $force{'wmctrl'} = 1 }, + 'wayland|wl' => sub { + $force{'wayland'} = 1;}, + 'wm|wmctrl' => sub { + $force{'wmctrl'} = 1;}, '<>' => sub { my ($opt) = @_; main::error_handler('unknown-option', "$opt", "");} @@ -5226,14 +5355,15 @@ sub post_process { !$show{'partition-full'} && !$show{'swap'} && !$show{'unmounted'}){ main::error_handler('bad-arg', '-l/-u', 'missing required option(s) -j, -o, -p, -P'); } - $show{'graphic-basic'} = 0 if $b_admin; + $extra = 3 if $b_admin; + $show{'graphic-basic'} = 0 if $show{'graphic-full'} && $extra > 1; if ($use{'sensors-default'}){ @sensors_exclude = (); @sensors_use = (); } if ($show{'short'} || $show{'disk'} || $show{'disk-basic'} || $show{'disk-total'} || - $show{'logical'} || $show{'partition'} || $show{'partition-full'} || $show{'raid'} || - $show{'unmounted'}){ + $show{'logical'} || $show{'partition'} || $show{'partition-full'} || $show{'raid'} || + $show{'unmounted'}){ $use{'block-tool'} = 1; } if ($show{'short'} || $show{'raid'} || $show{'disk'} || $show{'disk-total'} || @@ -5246,11 +5376,10 @@ sub post_process { } # triggers may extend to -D, -pP if ($show{'short'} || $show{'logical'} || $show{'raid'} || $show{'disk'} || - $show{'disk-total'} || $show{'disk-basic'} || $show{'unmounted'}){ + $show{'disk-total'} || $show{'disk-basic'} || $show{'unmounted'}){ $use{'logical'} = 1; } main::set_sudo() if ($show{'unmounted'} || ($extra > 0 && $show{'disk'})); - $extra = 3 if $b_admin; $use{'filter'} = 0 if $use{'filter-override'}; # override for things like -b or -v2 to -v3 $show{'cpu-basic'} = 0 if $show{'cpu'}; @@ -5404,8 +5533,8 @@ sub show_options { (except -J, -W) plus --swap, -s and -n. Does not show extra verbose options such as -d -f -i -J -l -m -o -p -r -t -u -x, unless specified."], ['1', '-G', '--graphics', "Graphics info (devices(s), drivers, display - protocol (if available), display server/Wayland compositor, resolution, - renderer, OpenGL version)."], + protocol (if available), display server/Wayland compositor, resolution, X.org: + renderer, OpenGL version; Xvesa: VBE info."], ['1', '-i', '--ip', "WAN IP address and local interfaces (requires ifconfig or ip network tool). Triggers -n. Not shown with -F for user security reasons. You shouldn't paste your local/WAN IP."], @@ -5560,7 +5689,7 @@ sub show_options { verbose or line output, not short form); check man page for explanations!; also sets --extra=3:"], ['2', '-A', '', "If available: list of alternate kernel modules/drivers - for device(s)."], + for device(s); PCIe lanes-max: gen, speed, lanes (if relevant)."], ['2', '-C', '', "If available: CPU socket type, base/boost speeds (dmidecode+root/sudo/doas required); Full topology line, with cores, threads, threads per core, granular cache data, smt status; CPU vulnerabilities (bugs); @@ -5570,10 +5699,9 @@ sub show_options { family; maj:min, USB drive specifics; SMART report."], ['2', '-E', '', "If available: in Report:, adds Info: line: acl-mtu, sco-mtu, link-policy, link-mode, service-classes."], - ['2', '-G', '', "If available: Xorg Display ID, Screens total, default Screen, - current Screen; per X Screen: resolution, dpi, size, diagonal; per Monitor: - resolution; hz; dpi; size; diagonal; list of alternate kernel modules/drivers - for device(s)."], + ['2', '-G', '', "PCIe lanes-max: gen, speed, lanes (if relevant); list of + alternate kernel modules/drivers for device(s) (if available); Monitor built + year, gamma, screen ratio (if available)."], ['2', '-I', '', "As well as per package manager counts, also adds total number of lib files found for each package manager if not -r; adds init service tool."], @@ -5582,7 +5710,7 @@ sub show_options { ['2', '-L', '', "LV, Crypto, devices, components: add maj:min; show full device/components report (speed, mapped names)."], ['2', '-n,-N', '', "If available: list of alternate kernel modules/drivers - for device(s)."], + for device(s); PCIe lanes-max: gen, speed, lanes (if relevant)."], ['2', '-o', '', "If available: maj:min of device."], ['2', '-p,-P', '', "If available: raw size of ${partition_string}s, maj:min, percent available for user, block size of file system (root required)."], @@ -5611,9 +5739,9 @@ sub show_options { Example:^^ALL^=^NOPASSWD:^/usr/sbin/hddtemp"], ['2', '-E', '', "PCI/USB Bus ID of device, driver version, LMP version."], - ['2', '-G', '', "Specific vendor/product information (if relevant); - PCI/USB ID of device; Direct rendering status (in X); Screen - number GPU is running on (Nvidia only)."], + ['2', '-G', '', "Specific vendor/product information (if relevant); PCI/USB ID + of device; Direct rendering status (in X); Screen number GPU is running on + (Nvidia only)."], ['2', '-i', '', "For IPv6, show additional scope addresses: Global, Site, Temporary, Unknown. See --limit for large counts of IP addresses."], ['2', '-I', '', "Default system GCC. With -xx, also shows other installed @@ -5648,16 +5776,20 @@ sub show_options { ['0', '', '', ''], ['1', '-xx', '--extra 2', "Show extra, extra data (only works with verbose or line output, not short form):"], - ['2', '-A', '', "Chip vendor:product ID for each audio device."], + ['2', '-A', '', "Chip vendor:product ID for each audio device; PCIe speed, + lanes (if found)."], ['2', '-B', '', "Serial number."], ['2', '-D', '', "Disk transfer speed; NVMe lanes; Disk serial number; LVM volume group free space (if available); disk duid (some BSDs)."], - ['2', '-E', '', "Chip vendor:product ID, LMP subversion."], - ['2', '-G', '', "Chip vendor:product ID for each video device; OpenGL - compatibility version, if free drivers and available; Xorg compositor; - alternate Xorg drivers (if available). Alternate means driver is on automatic - driver check list of Xorg for the device vendor, but is not installed on - system; Xorg dpi."], + ['2', '-E', '', "Chip vendor:product ID, LMP subversion; PCIe speed, lanes + (if found)."], + ['2', '-G', '', "Chip vendor:product ID for each video device; Output ports, + used and empty; PCIe speed, lanes (if found); Xorg: OpenGL compatibility + version, if free drivers and available; Xorg compositor; alternate Xorg + drivers (if available. Alternate means driver is on automatic driver check + list of Xorg for the device vendor, but is not installed on system); Xorg + Screen data: ID, s-res, dpi; Monitors: ID, position (if > 1), resolution, + dpi, model, diagonal."], ['2', '-I', '', "Other detected installed gcc versions (if present). System default runlevel. Adds parent program (or pty/tty) for shell info if not in IRC. Adds Init version number, RC (if found). Adds per package manager @@ -5670,7 +5802,7 @@ sub show_options { ['2', '-m,--memory-modules', '', "Manufacturer, part number; single/double bank (if found)."], ['2', '-M', '', "Chassis info, BIOS ROM size (dmidecode only), if available."], - ['2', '-N', '', "Chip vendor:product ID."], + ['2', '-N', '', "Chip vendor:product ID; PCIe speed, lanes (if found)."], ['2', '-r', '', "Packages, see -Ixx."], ['2', '-R', '', "md-raid: Superblock (if present), algorithm. If resync, shows progress bar. Hardware RAID Chip vendor:product ID."], @@ -5697,7 +5829,8 @@ sub show_options { ['2', '-D', '', "Firmware rev. if available; partition scheme, in some cases; disk type, rotation rpm (if available)."], ['2', '-E', '', "Serial number, class ID, HCI version and revision."], - ['2', '-G', '', "Serial number, class ID."], + ['2', '-G', '', "Device serial number, class ID; Xorg Screen size, diag; + Monitors: hz, size, modes, serial, scale, modes (max/min)."], ['2', '-I', '', "For 'Shell:' adds ([doas|su|sudo|login]) to shell name if present; adds default shell+version if different; for 'running in:' adds (SSH) if SSH session; adds wakeups: (from suspend) to Uptime."], @@ -6088,10 +6221,10 @@ sub get_client_version { # NOTE: these must be empirically determined, not all events that # show no tty are actually IRC. tmux is not a vt, but runs inside one if (!$client{'name-print'}){ - my $wl_terms = 'alacritty|altyo|black-screen|conhost|doas|evilvte|'; - $wl_terms .= 'germinal|guake|havoc|hyper|kate|kitty|kmscon|konsole|login|'; - $wl_terms .= 'macwise|minicom|putty|rxvt|sakura|securecrt|shellinabox|'; - $wl_terms .= '^st$|sudo|term|tilda|tilix|tmux|tym|wayst|xiki|'; + my $wl_terms = 'alacritty|altyo|\bate\b|black-screen|conhost|doas|evilvte|'; + $wl_terms .= 'foot|germinal|guake|havoc|hyper|kate|kitty|kmscon|konsole|'; + $wl_terms .= 'login|macwise|minicom|putty|rxvt|sakura|securecrt|'; + $wl_terms .= 'shellinabox|^st$|sudo|term|tilda|tilix|tmux|tym|wayst|xiki|'; $wl_terms .= 'yaft|yakuake|\bzoc\b'; my $wl_clients = 'ansible|chef|run-parts|sshd'; my $whitelist = "$wl_terms|$wl_clients"; @@ -6121,7 +6254,7 @@ sub get_cmdline { } local $\ = ''; open(my $fh, '<', "/proc/$ppid/cmdline") or - print_line("Open /proc/$ppid/cmdline failed: $!"); + print_line("Open /proc/$ppid/cmdline failed: $!"); my @rows = <$fh>; close $fh; foreach (@rows){ @@ -6282,7 +6415,7 @@ sub clean { my ($item) = @_; return $item if !$item;# handle cases where it was 0 or '' or undefined # note: |nee trips engineering, but I don't know why nee was filtered - $item =~ s/chipset|company|components|computing|computer|corporation|communications|electronics|electrical|electric|gmbh|group|incorporation|industrial|international|\bnee\b|no\sstring|revision|semiconductor|software|technologies|technology|ltd\.||\bltd\b|inc\.||\binc\b|intl\.|co\.||corp\.||\(tm\)|\(r\)|®|\(rev ..\)|\'|\"|\sinc\s*$|\?//gi; + $item =~ s/chipset|company|components|computing|computer|corporation|communications|electronics?|electric(al)?|group|incorporation|industrial|international|limited|\bnee\b|?|revision|semiconductor|software|technolog(ies|y)|?|ltd\.||\bltd\b|inc\.||\binc\b|intl\.|co\.||corp\.||\(tm\)|\(r\)|®|\(rev ..\)|\'|\"|\?//gi; $item =~ s/,|\*/ /g; $item =~ s/^\s+|\s+$//g; $item =~ s/\s\s+/ /g; @@ -6515,24 +6648,25 @@ sub message { 'cpu-speeds' => 'No per core speed data found.', 'cpu-speeds-bsd' => 'No OS support for core speeds.', 'darwin-feature' => 'Feature not supported iu Darwin/OSX.', + 'dev' => 'Feature under development', 'disk-data' => 'No disk data found.', 'disk-data-bsd' => 'No disk data found.', 'disk-size-0' => 'Total N/A', - 'display-console' => 'No advanced graphics data found on this system in console.', - 'display-driver-na' => 'n/a (using device driver)', - 'display-null' => 'No advanced graphics data found on this system.', - 'display-root' => 'Advanced graphics data unavailable in console for root.', - 'display-root-x' => 'Advanced graphics data unavailable for root.', + 'display-driver-na' => ' X driver n/a', 'display-server' => 'No display server data found. Headless machine?', - 'glxinfo-missing' => 'Unable to show advanced data. Required tool glxinfo missing.', - 'gl-empty' => 'Unset. Missing GL driver?', - 'display-try' => 'Advanced graphics data unavailable in console. Try -G --display', - 'dev' => 'Feature under development', 'dmesg-boot-permissions' => 'dmesg.boot permissions', 'dmesg-boot-missing' => 'dmesg.boot not found', - 'IP' => "No $id found. Connected to web? SSL issues?", 'dmidecode-dev-mem' => 'dmidecode is not allowed to read /dev/mem', 'dmidecode-smbios' => 'No SMBIOS data for dmidecode to process', + 'gl-console' => 'No advanced graphics data found on this system in console.', + 'gl-empty' => 'Unset. Missing GL driver?', + 'gl-null' => 'No GL data found on this system.', + 'gl-root' => 'GL data unavailable in console for root.', + 'gl-root-display' => 'GL data unavailable for root.', + 'gl-try' => 'GL data unavailable in console. Try -G --display', + 'glxinfo-missing' => 'Unable to show GL data. Required tool glxinfo missing.', + 'interface-wayland' => 'Wayland GBM/EGL data currently not available.', + 'IP' => "No $id found. Connected to web? SSL issues?", 'IP-dig' => "No $id found. Connected to web? SSL issues? Try --no-dig", 'IP-no-dig' => "No $id found. Connected to web? SSL issues? Try enabling dig", 'logical-data' => 'No logical block device data found.', @@ -6541,6 +6675,11 @@ sub message { 'machine-data-bsd' => 'No machine data: Is dmidecode installed? Try -M --dmidecode.', 'machine-data-dmidecode' => 'No machine data: try newer kernel. Is dmidecode installed? Try -M --dmidecode.', 'machine-data-force-dmidecode' => 'No machine data: try newer kernel. Is dmidecode installed? Try -M --dmidecode.', + 'machine-data-fruid' => 'No machine data: Is fruid_print installed?', + 'monitor-console' => 'N/A in console', + 'monitor-id' => 'not-matched', + 'monitor-na' => 'N/A', + 'monitor-wayland' => 'no compositor data', 'note-check' => 'check', 'note-est' => 'est.', 'optical-data' => 'No optical or floppy data found.', @@ -6569,6 +6708,8 @@ sub message { 'root-item-incomplete' => "Full $id report requires superuser permissions.", 'root-required' => '', 'root-suggested' => 'try sudo/root',# gdm only + 'screen-wayland' => 'no compositor data', + 'screen-xvesa' => 'no Xvesa data', 'sensors-data-bsd' => "$id sensor data found but not usable.", 'sensors-data-bsd-ok' => 'No sensor data found. Are sensors present?', 'sensors-data-ipmi' => 'No ipmi sensor data found.', @@ -6602,6 +6743,7 @@ sub message { 'unknown-shell' => 'ERR-100', 'weather-error' => "Error: $id", 'weather-null' => "No $id found. Internet connection working?", + 'xvesa-interface' => 'No Xvesa VBE/GOP data found.', ); return $unfound{$type}; } @@ -6619,16 +6761,16 @@ sub regex_range { return join('|',@processed); } +# Handles duplicates occuring anywhere in string sub remove_duplicates { my ($string) = @_; return if !$string; - my $holder = ''; - my (@temp); + my (%holder,@temp); foreach (split(/\s+/, $string)){ - if ($holder ne $_){ + if (!$holder{lc($_)}){ push(@temp, $_); + $holder{lc($_)} = 1; } - $holder = $_; } $string = join(' ', @temp); return $string; @@ -6710,16 +6852,12 @@ sub generate_json { my ($b_cpanel,$b_valid); error_handler('not-in-irc', 'help') if $b_irc; print Dumper $data if $b_debug; - if (check_perl_module('Cpanel::JSON::XS')){ - Cpanel::JSON::XS->import; - $json = Cpanel::JSON::XS::encode_json($data); - } - elsif (check_perl_module('JSON::XS')){ - JSON::XS->import; - $json = JSON::XS::encode_json($data); + load_json() if !$loaded{'json'}; + if ($use{'json'}){ + $json = &{$use{'json'}->{'encode'}}($data); } else { - error_handler('required-module', 'json', 'Cpanel::JSON::XS OR JSON::XS'); + error_handler('required-module', 'json', 'JSON::PP, Cpanel::JSON::XS or JSON::XS'); } if ($json){ #$json =~ s/"[0-9]+#/"/g; @@ -7174,7 +7312,7 @@ sub get { push(@rows,device_output()); } if (((%risc && !$use{'soc-audio'} && !$use{'pci-tool'}) || !@rows) && - (my $file = $system_files{'asound-cards'})){ + (my $file = $system_files{'asound-cards'})){ push(@rows,asound_output($file)); } push(@rows,usb_output()); @@ -7228,7 +7366,11 @@ sub device_output { $rows[$j]->{main::key($num++,0,3,'alternate')} = $row->[10] if $row->[10]; } if ($extra > 0){ - $rows[$j]->{main::key($num++,0,2,'bus-ID')} = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + my $bus_id = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + if ($extra > 1 && $bus_id ne 'N/A'){ + main::get_pcie_data($bus_id,$j,\@rows,\$num); + } + $rows[$j]->{main::key($num++,0,2,'bus-ID')} = $bus_id; } if ($extra > 1){ my $chip_id = main::get_chip_id($row->[5],$row->[6]); @@ -7466,7 +7608,7 @@ sub get { @rows = ({main::key($num++,0,1,$key1) => $val1,}); } } - (@upower_items,$b_upower,$upower) = undef; + (@upower_items,$b_upower,$upower) = (); eval $end if $b_log; return @rows; } @@ -8013,7 +8155,11 @@ sub device_output { $rows[$j]->{main::key($num++,0,3,'alternate')} = $row->[10] if $row->[10]; } if ($extra > 0){ - $rows[$j]->{main::key($num++,0,2,'bus-ID')} = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + $bus_id = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + if ($extra > 1 && $bus_id ne 'N/A'){ + main::get_pcie_data($bus_id,$j,\@rows,\$num); + } + $rows[$j]->{main::key($num++,0,2,'bus-ID')} = $bus_id; } if ($extra > 1){ my $chip_id = main::get_chip_id($row->[5],$row->[6]); @@ -8191,7 +8337,7 @@ sub advanced_output { # } # this data only from hciconfig if ($b_admin && - ($hci{$item}->{'acl-mtu'} || $hci{$item}->{'sco-mtu'} || $hci{$item}->{'link-policy'})){ + ($hci{$item}->{'acl-mtu'} || $hci{$item}->{'sco-mtu'} || $hci{$item}->{'link-policy'})){ $j = scalar @rows; push(@rows,{ main::key($num++,1,$l,'Info') => '', @@ -8217,7 +8363,7 @@ sub advanced_output { } } if (!@rows && !$b_hci_error && ($alerts{'hciconfig'}->{'action'} ne 'use' && - $alerts{'bt-adapter'}->{'action'} ne 'use')){ + $alerts{'bt-adapter'}->{'action'} ne 'use')){ my $key = 'Report'; my $value = ''; if ($alerts{'hciconfig'}->{'action'} eq 'platform' || @@ -8883,7 +9029,7 @@ sub cpuinfo_data { } } cpuinfo_data_grabber($file,\$cpu{'type'}) if !$loaded{'cpuinfo'}; - $cpu{'type'} = cpu_vendor($cpu_arch) if $cpu_arch =~ /e2k/; # already set to lower + $cpu{'type'} = cpu_vendor($cpu_arch) if $cpu_arch eq 'elbrus'; # already set to lower my ($core_count,$proc_count,$speed) = (0,0,0); my ($b_block_1) = (1); # need to prime for arm cpus, which do not have physical/core ids usually @@ -9504,7 +9650,7 @@ sub sysctl_data { # darwin shows machine, like MacBook7,1, not cpu # machdep.cpu.brand_string: Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz if (($bsd_type ne 'darwin' && $line[0] eq 'hw.model') || - $line[0] eq 'machdep.cpu.brand_string'){ + $line[0] eq 'machdep.cpu.brand_string'){ # cut L2 cache/cpu max speed out of model string, if available # openbsd 5.6: AMD Sempron(tm) Processor 3400+ ("AuthenticAMD" 686-class, 256KB L2 cache) # openbsd 6.x has Lx cache data in dmesg.boot @@ -9835,7 +9981,7 @@ sub dmidecode_data { # Older dmidecode appear to have unreliable Upgrade outputs elsif ($item->[0] == 4){ # skip first three row,s we don't need that data - ($socket,$upgrade) = (undef,undef); + ($socket,$upgrade) = (); $dmi_data{'phys-cnt'}++; # try to catch bsds without physical cpu count foreach my $value (@$item[3 .. $#$item]){ next if $value =~ /^~/; @@ -9872,11 +10018,11 @@ sub dmidecode_data { # Seen older cases where Upgrade: Other value exists if ($socket || $upgrade){ if ($socket && $upgrade){ - $upgrade = undef if $socket eq $upgrade; + undef $upgrade if $socket eq $upgrade; } elsif ($upgrade){ $socket = $upgrade; - $upgrade = undef; + undef $upgrade; } $dmi_data{'socket'} = $socket; $dmi_data{'upgrade'} = $upgrade; @@ -10074,7 +10220,7 @@ sub cp_data_dmi { } # We have to undef all the sys stuff to get back to the true dmidecode results # Too many variants to treat one by one, just clear it out if forced. - undef %$caches if $force{'dmidecode'}; + undef $caches if $force{'dmidecode'}; # We don't want to use dmi L1/L2/L3 at all for non BSD systems unless forced # because have seen totally gibberish dmidecode data for caches. /sys cache # data preferred, more granular and basically consistently right. @@ -10409,7 +10555,7 @@ sub cp_cache_desc { $desc .= $sep . $cache_desc->{$_} . 'x' . main::get_size($_,'string'); $sep = ', '; } - undef %{$cache_desc}; + undef $cache_desc; return $desc; } # $caches passed by reference @@ -10758,8 +10904,10 @@ sub cp_cpu_arch { $arch = 'Core Merom'} elsif ($model =~ /^(15)$/){ $arch = 'M Tolapai'} # pentium M system on chip - elsif ($model =~ /^(17|1D)$/){ + elsif ($model =~ /^(1D)$/){ $arch = 'Core Penryn'} + elsif ($model =~ /^(17)$/){ + $arch = 'Core Yorkfield'} # had 25 also, but that's westmere, at least for stepping 2 elsif ($model =~ /^(1A|1E|1F|2C|2E|2F)$/){ $arch = 'Nehalem'} @@ -11243,7 +11391,7 @@ sub cpu_vendor { elsif ($string =~ /centaur/){ $vendor = "centaur" } - elsif ($string =~ /(e2k|elbrus)/){ + elsif ($string eq 'elbrus'){ $vendor = "elbrus" } eval $end if $b_log; @@ -11344,9 +11492,9 @@ sub hex_and_decimal { ## DriveItem { package DriveItem; -my ($b_hddtemp,$b_nvme,$smartctl_missing); +my ($b_hddtemp,$b_nvme,$smartctl_missing,$vendors); my ($hddtemp,$nvme) = ('',''); -my (@by_id,@by_path,@vendors); +my (@by_id,@by_path); my ($debugger_dir); # main::writer("$debugger_dir/system-repo-data-urpmq.txt",\@data2) if $debugger_dir; sub get { @@ -11396,8 +11544,8 @@ sub get { if ($show{'optical'} || $show{'optical-basic'}){ push(@rows,OpticalItem::get()); } - ($b_hddtemp,$b_nvme,$hddtemp,$nvme) = (undef,undef,undef,undef); - (@by_id,@by_path) = (undef,undef); + ($b_hddtemp,$b_nvme,$hddtemp,$nvme,$vendors) = (); + (@by_id,@by_path) = (); eval $end if $b_log; return @rows; } @@ -11860,10 +12008,10 @@ sub proc_data_advanced { $drives->[$i]{'drive-type'} = 'HDD'; } elsif (($block_type && $block_type ne 'sdx') || - # note: this case could conceivabley be wrong for a spun down HDD - (defined $data[2] && $data[2] eq '0') || - ($drives->[$i]{'model'} && - $drives->[$i]{'model'} =~ /(flash|mmc|msata|\bm[\.-]?2\b|nvme|ssd|solid\s?state)/i)){ + # note: this case could conceivabley be wrong for a spun down HDD + (defined $data[2] && $data[2] eq '0') || + ($drives->[$i]{'model'} && + $drives->[$i]{'model'} =~ /(flash|mmc|msata|\bm[\.-]?2\b|nvme|ssd|solid\s?state)/i)){ $drives->[$i]{'drive-type'} = 'SSD'; } } @@ -12223,7 +12371,7 @@ sub smartctl_data { # 'Airflow_Temperature_Cel' like: 29 (Min/Max 14/43) so can't use -1 index # Temperature like 29 Celsisu elsif ($split[$a] eq 'Temperature_Celsius' || $split[$a] eq 'Temperature' || - $split[$a] eq 'Airflow_Temperature_Cel'){ + $split[$a] eq 'Airflow_Temperature_Cel'){ if (!$data->[$i]{'temp'} && $split[$r]){ $data->[$i]{'temp'} = $split[$r]; } @@ -12600,9 +12748,10 @@ sub disk_data_by_id { # 0 - match pattern; 1 - replace pattern; 2 - vendor print; 3 - serial pattern sub set_vendors { eval $start if $b_log; - @vendors = ( + my $vendors = $_[0]; + @$vendors = ( ## MOST LIKELY/COMMON MATCHES ## - ['(Crucial|^(FC)?CT|-CT|^M4\b|Gizmo!|^((C300-)?CTF[\s-]?)?DDAC)','Crucial','Crucial',''], + ['(Crucial|^(FC)?CT|-CT|^M4(\b|SSD)|Gizmo!|^((C300-)?CTF[\s-]?)?DDAC)','Crucial','Crucial',''], # H10 HBRPEKNX0202A NVMe INTEL 512GB ['(\bINTEL\b|^SSD(PAM|SA2))','\bINTEL\b','Intel',''], # note: S[AV][1-9][0-9] can trigger false positives @@ -12677,6 +12826,7 @@ sub set_vendors { # must come before AP|Apacer ['^(APPLE|iPod)','^APPLE','Apple',''], ['^(AP|Apacer)','^Apacer','Apacer',''], + ['^(Apricom|SATAWire)','^Apricom','Apricom',''], ['^(A-?RAM|ARSSD)','^A-?RAM','A-RAM',''], ['^Arch','^Arch(\s*Memory)?','Arch Memory',''], ['^(Asenno|AS[1-9])','^Asenno','Asenno',''], @@ -12686,6 +12836,7 @@ sub set_vendors { ['^Axiom','^Axiom','Axiom',''], ['^(Baititon|BT[0-9])','^Baititon','Baititon',''], ['^Bamba','^Bamba','Bamba',''], + ['^(Beckhoff)','^Beckhoff','Beckhoff',''], ['^Bell\b','^Bell','Packard Bell',''], ['^(BelovedkaiAE|GhostPen)','^BelovedkaiAE','BelovedkaiAE',''], ['^BHT','^BHT','BHT',''], @@ -12693,6 +12844,7 @@ sub set_vendors { ['^BIOSTAR','^BIOSTAR','Biostar',''], ['^BIWIN','^BIWIN','BIWIN',''], ['^Blackpcs','^Blackpcs','Blackpcs',''], + ['^(BlitzWolf|BW-?PSSD)','^BlitzWolf','BlitzWolf',''], ['^(BlueRay|SDM[0-9])','^BlueRay','BlueRay',''], ['^Bory','^Bory','Bory',''], ['^Braveeagle','^Braveeagle','BraveEagle',''], @@ -12725,6 +12877,7 @@ sub set_vendors { ['^Derler','^Derler','Derler',''], ['^detech','^detech','DETech',''], ['^DGM','^DGM\b','DGM',''], + ['^(DICOM|MAESTRO)','^DICOM','DICOM',''], ['^Digifast','^Digifast','Digifast',''], ['^DIGITAL\s?FILM','DIGITAL\s?FILM','Digital Film',''], ['^Dikom','^Dikom','Dikom',''], @@ -12752,6 +12905,7 @@ sub set_vendors { # NOTE: ESA3... may be IBM PCIe SAD card/drives ['^(EXCELSTOR|r technology)','^EXCELSTOR( TECHNO(LOGY)?)?','ExcelStor',''], ['^EYOTA','^EYOTA','EYOTA',''], + ['^EZCOOL','^EZCOOL','EZCOOL',''], ['^EZLINK','^EZLINK','EZLINK',''], ['^Fantom','^Fantom( Drive[s]?)?','Fantom Drives',''], ['^Fanxiang','^Fanxiang','Fanxiang',''], @@ -12766,6 +12920,7 @@ sub set_vendors { ['^(FOXLINE|FLD)','^FOXLINE','Foxline',''], # russian vendor? ['^(GALAX\b|Gamer\s?L)','^GALAX','GALAX',''], ['^Galaxy\b','^Galaxy','Galaxy',''], + ['^Gamer[_\s-]?Black','^Gamer[_\s-]?Black','Gamer Black',''], ['^(Garmin|Fenix|Nuvi|Zumo)','^Garmin','Garmin',''], ['^Geil','^Geil','Geil',''], ['^GelL','^GelL','GelL',''], # typo for Geil? GelL ZENITH R3 120GB @@ -12780,6 +12935,8 @@ sub set_vendors { ['^GLOWY','^GLOWY','Glowy',''], ['^Goldendisk','^Goldendisk','Goldendisk',''], ['^Goldenfir','^Goldenfir','Goldenfir',''], + ['^Golden[\s_-]?Memory','^Golden[\s_-]?Memory','Golden Memory',''], + ['^(Goldkey|GKP)','^Goldkey','GoldKey',''], # Wilk Elektronik SA, poland ['^(Wilk\s*)?(GOODRAM|GOODDRIVE|IR[\s-]?SSD|IRP|SSDPR)','^GOODRAM','GOODRAM',''], # supertalent also has FM: |FM @@ -12791,6 +12948,7 @@ sub set_vendors { ['^HDC','^HDC\b','HDC',''], ['^Hectron','^Hectron','Hectron',''], ['^HEMA','^HEMA','HEMA',''], + ['(HEORIADY|^HX-0)','^HEORIADY','HEORIADY',''], ['^(Hikvision|HKVSN|HS-SSD)','^Hikvision','Hikvision',''], ['^Hoodisk','^Hoodisk','Hoodisk',''], ['^HUAWEI','^HUAWEI','Huawei',''], @@ -12801,6 +12959,7 @@ sub set_vendors { ['^IEI Tech','^IEI Tech(\.|nology)?( Corp(\.|oration)?)?','IEI Technology',''], ['^(IGEL|UD Pocket)','^IGEL','IGEL',''], ['^(Imation|Nano\s?Pro|HQT)','^Imation(\sImation)?','Imation',''], # Imation_ImationFlashDrive; TF20 is imation/tdk + ['^(IMC|Kanguru)','^IMC\b','IMC',''], ['^(Inateck|FE20)','^Inateck','Inateck',''], ['^(Inca\b|Npenterprise)','^Inca','Inca',''], ['^(Indilinx|IND-)','^Indilinx','Indilinx',''], @@ -12812,6 +12971,7 @@ sub set_vendors { ['(Innostor|1f75)','(Innostor|1f75)','Innostor',''], ['(^Innovation|Innovation\s?IT)','Innovation(\s*IT)?','Innovation IT',''], ['^Innovera','^Innovera','Innovera',''], + ['^(I\.?norys|INO-?IH])','^I\.?norys','I.norys',''], ['^Intaiel','^Intaiel','Intaiel',''], ['^(INM|Integral|V\s?Series)','^Integral(\s?Memory)?','Integral Memory',''], ['^(lntenso|Intenso|(Alu|Basic|Business|Micro|c?Mobile|Premium|Rainbow|Slim|Speed|Twister|Ultra) Line|Rainbow)','^Intenso','Intenso',''], @@ -12819,6 +12979,7 @@ sub set_vendors { ['^(Integrated[\s-]?Technology|IT[0-9]+)','^Integrated[\s-]?Technology','Integrated Technology',''], ['^(Iomega|ZIP\b|Clik!)','^Iomega','Iomega',''], ['^ISOCOM','^ISOCOM','ISOCOM (Shenzhen Longsys Electronics)',''], + ['^(Jaster|JS\d)','^Jaster','Jaster',''], ['^JingX','^JingX','JingX',''], #JingX 120G SSD - not confirmed, but guessing ['^Jingyi','^Jingyi','Jingyi',''], # NOTE: ITY2 120GB hard to find @@ -12828,11 +12989,11 @@ sub set_vendors { ['^Kingbank','^Kingbank','Kingbank',''], ['^Kingchux[\s-]?ing','^Kingchux[\s-]?ing','Kingchuxing',''], ['(KingDian|^NGF)','KingDian','KingDian',''], - ['^Kingfast','^Kingfast','Kingfast',''], + ['^(Kingfast|TYFS)','^Kingfast','Kingfast',''], ['^KingMAX','^KingMAX','KingMAX',''], ['^Kingrich','^Kingrich','KingrSU04Gich',''], ['KING\s?SHA\s?RE','KING\s?SHA\s?RE','KingShare',''], - ['^(KingSpec|ACSC|KS[DQ]|N[ET]-[0-9]|P4\b|PA18|T-(3260|64|128))','^KingSpec','KingSpec',''], + ['^(KingSpec|ACSC|KS[DQ]|N[ET]-[0-9]|P4\b|PA[_-]?(18|25)|T-(3260|64|128))','^KingSpec','KingSpec',''], ['^KingSSD','^KingSSD','KingSSD',''], # kingwin docking, not actual drive ['^(EZD|EZ-Dock)','','Kingwin Docking Station',''], @@ -12858,9 +13019,11 @@ sub set_vendors { ['(LITE[-\s]?ON|^PH[1-9])','LITE[-]?ON','LITE-ON',''], # PH6-CE240-L; CL1-3D256-Q11 NVMe LITEON 256GB ['^LONDISK','^LONDISK','LONDISK',''], ['^Longline','^Longline','Longline',''], + ['^LuminouTek','^LuminouTek','LuminouTek',''], ['^(LSI|MegaRAID)','^LSI\b','LSI',''], ['^(M-Systems|DiskOnKey)','^M-Systems','M-Systems',''], ['^(Mach\s*Xtreme|MXSSD|MXU|MX[\s-])','^Mach\s*Xtreme','Mach Xtreme',''], + ['^Mainic','^Mainic','Mainic',''], ['^Maximus','^Maximus','Maximus',''], ['^Maxone','^Maxone','Maxone',''], ['^(MAXTOR|Atlas|L(250|500)|TM[0-9]{4}|[KL]0[1-9]|Y[0-9]{3}[A-Z]|STM[0-9]|F[0-9]{3}L)','^MAXTOR','Maxtor',''], # note M2 M3 is usually maxtor, but can be samsung @@ -12921,6 +13084,7 @@ sub set_vendors { ['^(PQI|Intelligent\s?Stick|Cool\s?Drive)','^PQI','PQI',''], ['^(Premiertek|QSSD|Quaroni)','^Premiertek','Premiertek',''], ['^(Pretec|UltimateGuard)','Pretec','Pretec',''], + ['^(Prolific)','^Prolific( Technolgy Inc\.)?','Prolific',''], # PS3109S9 is the result of an error condition with ssd drive ['QEMU','^[0-9]*QEMU( QEMU)?','QEMU',''], # 0QUEMU QEMU HARDDISK ['(^Quantum|Fireball)','^Quantum','Quantum',''], @@ -12957,13 +13121,15 @@ sub set_vendors { ['^Skill','^Skill','Skill',''], ['^(SMART( Storage Systems)?|TX)','^(SMART( Storage Systems)?)','Smart Storage Systems',''], ['^Sobetter','^Sobetter','Sobetter',''], - ['^(S[FR]-|Sony)','^Sony','Sony',''], + ['^(S[FR]-|Sony|IM9)','^Sony','Sony',''], ['^(SSSTC|CL1-)','^SSSTC','SSSTC',''], ['^STE[CK]','^STE[CK]','sTec',''], # wd bought this one ['^STmagic','^STmagic','STmagic',''], ['^STORFLY','^STORFLY','StorFly',''], ['\dSUN\d','^SUN(\sMicrosystems)?','Sun Microsystems',''], + ['^Sundisk','^Sundisk','Sundisk',''], ['^SUNEAST','^SUNEAST','SunEast',''], + ['^Supersonic','^Supersonic','Supersonic',''], ['^SuperSSpeed','^SuperSSpeed','SuperSSpeed',''], # NOTE: F[MNETU] not reliable, g.skill starts with FM too: # Seagate ST skips STT. @@ -12985,12 +13151,12 @@ sub set_vendors { ['^TEUTONS','^TEUTONS','TEUTONS',''], ['^THU','^THU','THU',''], ['^Tigo','^Tigo','Tigo',''], - ['^Timetec','^Timetec','Timetec',''], + ['^(Timetec|35TT)','^Timetec','Timetec',''], ['^TKD','^TKD','TKD',''], ['^TopSunligt','^TopSunligt','TopSunligt',''], # is this a typo? hard to know ['^TopSunlight','^TopSunlight','TopSunlight',''], ['^TOROSUS','^TOROSUS','Torosus',''], - ['^([F]?TS|Transcend|JetDrive|JetFlash|USDU|EZEX|1307)','^(Transcend|1307)','Transcend',''], + ['(^[F]?TS|Transcend|JetDrive|JetFlash|^USDU|^EZEX|^1307)','^(Transcend|1307)','Transcend',''], ['^(TrekStor|DS (maxi|pocket)|DataStation)','^TrekStor','TrekStor',''], ['^Turbox','^Turbox','Turbox',''], ['^(TwinMOS|TW[0-9])','^TwinMOS','TwinMOS',''], @@ -13012,6 +13178,7 @@ sub set_vendors { ['^VMware','^VMware','VMware',''], ['^(Vseky|Vaseky)','^Vaseky','Vaseky',''], # ata-Vseky_V880_350G_ ['^(Walgreen|Infinitive)','^Walgreen','Walgreen',''], + ['^Walram','^Walram','WALRAM',''], ['^Walton','^Walton','Walton',''], ['^(Wearable|Air-?Stash)','^Wearable','Wearable',''], ['^Wellcomm','^Wellcomm','Wellcomm',''], @@ -13030,6 +13197,7 @@ sub set_vendors { ['^(YingChu|YGC)','^YingChu','YingChu',''], ['^(YUCUN|R880)','^YUCUN','YUCUN',''], ['^(ZALMAN|ZM\b)','^ZALMAN','Zalman',''], + ['^ZXIC','^ZXIC','ZXIC',''], ['^ZEUSLAP','^ZEUSLAP','ZEUSLAP',''], ['^(Zheino|CHN[0-9]|CNM)','^Zheino','Zheino',''], ['^(Zotac|ZTSSD)','^Zotac','Zotac',''], @@ -13048,12 +13216,15 @@ sub device_vendor { my ($vendor) = (''); my (@data); return if !$model; - set_vendors() if !@vendors; # 0 - match pattern; 1 - replace pattern; 2 - vendor print; 3 - serial pattern # Data URLs: inxi-resources.txt Section: DriveItem device_vendor() # $model = 'H10 HBRPEKNX0202A NVMe INTEL 512GB'; # $model = 'Patriot Memory'; - foreach my $row (@vendors){ + if (!$vendors){ + $vendors = []; + set_vendors($vendors); + } + foreach my $row (@$vendors){ if ($model =~ /$row->[0]/i || ($row->[3] && $serial && $serial =~ /$row->[3]/)){ $vendor = $row->[2]; # Usually we want to assign N/A at output phase, maybe do this logic there? @@ -13284,8 +13455,7 @@ sub device_speed { ## GraphicItem { package GraphicItem; -my $driver = ''; # we need this as a fallback in case no xorg log found -my %graphics; +my ($b_wayland_data,%graphics,$monitor_ids,$monitor_map); sub get { eval $start if $b_log; my (@rows); @@ -13312,16 +13482,19 @@ sub get { # note: not perfect, but we need usb gfx to show for all types, soc, pci, etc push(@rows,usb_output()); push(@rows,display_output()); - push(@rows,gl_output()); + push(@rows,interface_output()); + (%graphics,$monitor_ids,$monitor_map) = (); eval $end if $b_log; return @rows; } +## DEVICE OUTPUT ## sub device_output { eval $start if $b_log; return if !$devices{'graphics'}; my (@rows); my ($j,$num) = (0,1); + set_monitors_sys() if !$monitor_ids && -e '/sys/class/drm'; foreach my $row (@{$devices{'graphics'}}){ $num = 1; # print "$row->[0] $row->[3]\n"; @@ -13333,8 +13506,7 @@ sub device_output { next if $row->[3] != 0; # print "$row->[0] $row->[3]\n"; $j = scalar @rows; - $driver = $row->[9]; - $driver ||= 'N/A'; + push(@{$graphics{'gpu-drivers'}},$row->[9]) if $row->[9]; my $device = main::trimmer($row->[4]); $device = ($device) ? main::clean_pci($device,'output') : 'N/A'; # have seen absurdly verbose card descriptions, with non related data etc @@ -13348,6 +13520,7 @@ sub device_output { my $item = main::get_pci_vendor($row->[4],$row->[12]); $rows[$j]->{main::key($num++,0,2,'vendor')} = $item if $item; } + my $driver = ($row->[9]) ? $row->[9]:'N/A'; $rows[$j]->{main::key($num++,1,2,'driver')} = $driver; if ($row->[9] && !$bsd_type){ my $version = main::get_module_version($row->[9]); @@ -13359,7 +13532,14 @@ sub device_output { $rows[$j]->{main::key($num++,0,3,'alternate')} = $row->[10] if $row->[10]; } if ($extra > 0){ - $rows[$j]->{main::key($num++,0,2,'bus-ID')} = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + my $bus_id = (!$row->[2] && !$row->[3]) ? 'N/A' : "$row->[2].$row->[3]"; + if ($extra > 1 && $bus_id ne 'N/A'){ + main::get_pcie_data($bus_id,$j,\@rows,\$num,'gpu'); + } + if ($extra > 1 && $monitor_ids){ + port_output($bus_id,$j,\@rows,\$num); + } + $rows[$j]->{main::key($num++,0,2,'bus-ID')} = $bus_id; } if ($extra > 1){ my $chip_id = main::get_chip_id($row->[5],$row->[6]); @@ -13403,7 +13583,11 @@ sub usb_output { main::key($num++,0,2,'driver') => $driver, },); if ($extra > 0){ - $rows[$j]->{main::key($num++,0,2,'bus-ID')} = "$path_id:$row->[1]"; + my $bus_id = "$path_id:$row->[1]"; + if ($extra > 1 && $monitor_ids){ + port_output($bus_id,$j,\@rows,\$num); + } + $rows[$j]->{main::key($num++,0,2,'bus-ID')} = $bus_id; } if ($extra > 1){ $row->[7] ||= 'N/A'; @@ -13419,53 +13603,94 @@ sub usb_output { eval $end if $b_log; return @rows; } +# $rows, $num by ref +sub port_output { + my ($bus_id,$j,$rows,$num) = @_; + my (@connected,@disabled,@empty); + foreach my $id (keys %$monitor_ids){ + next if !$monitor_ids->{$id}{'status'}; + if ($monitor_ids->{$id}{'path'} =~ m|\Q$bus_id/drm/\E|){ + # status can be: connected|disconnected|unknown + if ($monitor_ids->{$id}{'status'} eq 'connected'){ + if ($monitor_ids->{$id}{'enabled'} eq 'enabled'){ + push(@connected,$id); + } + else { + push(@disabled,$id); + } + } + else { + push(@empty,$id); + } + } + } + if (@connected || @empty || @disabled){ + my ($off,$active,$unused); + my $split = ','; # add space if many to allow for wrapping + $$rows[$j]->{main::key($$num++,1,2,'ports')} = ''; + $split = ', ' if scalar @connected > 3; + $active = (@connected) ? join($split,sort @connected) : 'none'; + $$rows[$j]->{main::key($$num++,0,3,'active')} = $active; + if (@disabled){ + $split = (scalar @disabled > 3) ? ', ' : ','; + $off = join($split,sort @disabled); + $$rows[$j]->{main::key($$num++,0,3,'off')} = $off; + } + $split = (scalar @empty > 3) ? ', ' : ','; + $unused = (@empty) ? join($split,sort @empty) : 'none'; + $$rows[$j]->{main::key($$num++,0,3,'empty')} = $unused; + } +} + +## DISPLAY OUTPUT ## sub display_output(){ eval $start if $b_log; my (@row); - my ($num,$protocol) = (0,''); + my ($num,$j) = (0,0); # note: these may not always be set, they won't be out of X, for example - $protocol = get_protocol(); + display_protocol(); + $graphics{'protocol'} = 'wayland' if $force{'wayland'}; + # get rid of all inactive or disabled monitor port ids + set_active_monitors() if $monitor_ids; # note, since the compositor is the server with wayland, always show it - if ($extra > 1 || $protocol eq 'wayland'){ - set_compositor($protocol); + if ($extra > 1 || $graphics{'protocol'} eq 'wayland'){ + set_compositor_data(); } if ($b_display){ - display_data_x(); - # currently barebones, wayland needs a lot more work - if ($protocol && $protocol eq 'wayland' && !$graphics{'screens'}){ + # Add compositors as data sources found + if ($graphics{'protocol'} eq 'wayland'){ display_data_wayland(); - # it worked! we got screen data - $graphics{'no-xdpyinfo'} = undef if $graphics{'screens'}; + } + if (!$b_wayland_data){ + display_data_x() if !$force{'wayland'}; } } else { $graphics{'tty'} = tty_data(); } - # this gives better output than the failure last case, which would only show: - # for example: X.org: 1.9 instead of: X.org: 1.9.0 - $graphics{'x-version'} = $graphics{'xorg-version'} if $graphics{'xorg-version'};; - $graphics{'x-version'} = x_version() if !$graphics{'x-version'}; - $graphics{'x-version'} = $graphics{'x-version-id'} if !$graphics{'x-version'}; + # no xdpyinfo installed + # undef $graphics{'x-server'}; + if (!$graphics{'x-server'} || !$graphics{'x-server'}->[0][1] || + !$b_display || $graphics{'protocol'} eq 'wayland'){ + # fallback, will complete x-server/x-version, set flags, if found + display_server_data(); + } + if (!defined $graphics{'display-id'} && defined $ENV{'DISPLAY'}){ + $graphics{'display-id'} = $ENV{'DISPLAY'}; + } + # print Data::Dumper::Dumper $graphics{'x-server'}; # print Data::Dumper::Dumper \%graphics; if (%graphics){ - my ($driver_missing,$resolution,$server_string) = ('','',''); - # print "$graphics{'x-vendor'} $graphics{'x-version'} $graphics{'x-vendor-release'}","\n"; - if ($graphics{'x-vendor'}){ - my $version = ($graphics{'x-version'}) ? " $graphics{'x-version'}" : ''; - # $version = (!$version && $graphics{'x-vendor-release'}) ? " $graphics{'x-vendor-release'}" : ''; - $server_string = "$graphics{'x-vendor'}$version"; + my ($driver_note,$resolution,$server_string) = ('','',''); + my ($b_screen_monitors,$x_drivers); + $x_drivers = display_drivers_x() if !$force{'wayland'}; + # print "$graphics{'x-server'} $graphics{'x-version'} $graphics{'x-vendor-release'}","\n"; + if ($graphics{'x-server'}){ + $server_string = $graphics{'x-server'}->[0][0]; # print "$server_string\n"; } - elsif ($graphics{'x-version'}){ - if ($graphics{'x-version'} =~ /^Xvesa/){ - $server_string = $graphics{'x-version'}; - } - else { - $server_string = "X.org $graphics{'x-version'}"; - } - } - my @drivers = x_drivers(); - if (!$protocol && !$server_string && !$graphics{'x-vendor'} && !@drivers){ + if (!$graphics{'protocol'} && !$server_string && !$graphics{'x-server'} && + !$x_drivers){ $server_string = main::message('display-server'); @row = ({ main::key($num++,1,1,'Display') => '', @@ -13475,55 +13700,99 @@ sub display_output(){ else { $server_string ||= 'N/A'; @row = ({ - main::key($num++,1,1,'Display') => $protocol, - main::key($num++,0,2,'server') => $server_string, + main::key($num++,1,1,'Display') => $graphics{'protocol'}, + main::key($num++,1,2,'server') => $server_string, }); - if ($graphics{'compositor'}){ - $row[0]->{main::key($num++,0,2,'compositor')} = $graphics{'compositor'}; - if ($graphics{'compositor-version'}){ - $row[0]->{main::key($num++,0,3,'v')} = $graphics{'compositor-version'}; + if ($graphics{'x-server'} && $graphics{'x-server'}->[0][1]){ + $row[0]->{main::key($num++,0,3,'v')} = $graphics{'x-server'}->[0][1]; + } + if ($graphics{'x-server'} && $graphics{'x-server'}->[1][0]){ + $row[0]->{main::key($num++,1,3,'with')} = $graphics{'x-server'}->[1][0]; + if ($graphics{'x-server'}->[1][1]){ + $row[0]->{main::key($num++,0,4,'v')} = $graphics{'x-server'}->[1][1]; + } + } + if ($graphics{'compositors'}){ + if (scalar @{$graphics{'compositors'}} == 1){ + $row[0]->{main::key($num++,1,2,'compositor')} = $graphics{'compositors'}->[0][0]; + if ($graphics{'compositors'}->[0][1]){ + $row[0]->{main::key($num++,0,3,'v')} = $graphics{'compositors'}->[0][1]; + } + } + else { + my $i =1; + $row[0]->{main::key($num++,1,2,'compositors')} = ''; + foreach (@{$graphics{'compositors'}}){ + $row[0]->{main::key($num++,1,3,$i)} = $_->[0]; + if ($_->[1]){ + $row[0]->{main::key($num++,0,4,'v')} = $_->[1]; + } + $i++; + } } } # note: if no xorg log, and if wayland, there will be no xorg drivers, # obviously, so we use the last driver found on the card section in that case. # those come from lscpi kernel drivers so there should be no xorg/wayland issues. - if (!$drivers[0]){ + if (!$x_drivers || !$x_drivers->[0]){ # Fallback: specific case: in Arch/Manjaro gdm run systems, their Xorg.0.log is # located inside this directory, which is not readable unless you are root # Normally Arch gdm log is here: ~/.local/share/xorg/Xorg.1.log # $driver comes from the Device lines, and is just last fallback. - if ($driver && $driver ne 'N/A'){ - if (-e '/var/lib/gdm' && !$b_root){ - $driver_missing = main::message('display-driver-na') . ' - ' . main::message('root-suggested'); + if (!$graphics{'protocol'} || $graphics{'protocol'} ne 'wayland'){ + if ($graphics{'gpu-drivers'}){ + if (-e '/var/lib/gdm' && !$b_root){ + $driver_note = main::message('display-driver-na'); + $driver_note .= ' - ' . main::message('root-suggested'); + } + else { + $driver_note = main::message('display-driver-na'); + } } - else { - $driver_missing = main::message('display-driver-na'); + elsif (-e '/var/lib/gdm' && !$b_root) { + $driver_note = main::message('root-suggested'); } } - else { - $driver_missing = main::message('root-suggested') if -e '/var/lib/gdm' && !$b_root; - } + } + # if xvesa, will always have display-driver set + if ($graphics{'xvesa'} && $graphics{'display-driver'}){ + $row[0]->{main::key($num++,1,2,'driver')} = join(',',@{$graphics{'display-driver'}});; } else { - $driver = $drivers[0]; - } - $row[0]->{main::key($num++,1,2,'driver')} = ''; - $driver ||= 'N/A'; - $row[0]->{main::key($num++,1,3,'loaded')} = $driver; - if ($driver_missing){ - $row[0]->{main::key($num++,0,4,'note')} = $driver_missing; - } - if ($drivers[1]){ - $row[0]->{main::key($num++,0,3,'unloaded')} = $drivers[1]; - } - if ($drivers[2]){ - $row[0]->{main::key($num++,0,3,'failed')} = $drivers[2]; - } - if ($extra > 1 && $drivers[3]){ - $row[0]->{main::key($num++,0,3,'alternate')} = $drivers[3]; + $row[0]->{main::key($num++,1,2,'driver')} = ''; + # The only wayland setups with x drivers have xorg, transitional that is. + if ($x_drivers){ + $row[0]->{main::key($num++,1,3,'X')} = ''; + my $driver = ($x_drivers->[0]) ? $x_drivers->[0] : 'N/A'; + $row[0]->{main::key($num++,1,4,'loaded')} = $driver; + if ($x_drivers->[1]){ + $row[0]->{main::key($num++,0,4,'unloaded')} = $x_drivers->[1]; + } + if ($x_drivers->[2]){ + $row[0]->{main::key($num++,0,4,'failed')} = $x_drivers->[2]; + } + if ($extra > 1 && $x_drivers->[3]){ + $row[0]->{main::key($num++,0,4,'alternate')} = $x_drivers->[3]; + } + } + my $gpu_drivers = gpu_drivers_sys('all'); + my $drivers; + if ($gpu_drivers ){ + $drivers = join(',',@{$gpu_drivers}); + } + else { + $drivers = ($graphics{'gpu-drivers'}) ? join(',',@{$graphics{'gpu-drivers'}}): 'N/A'; + } + $row[0]->{main::key($num++,1,3,'gpu')} = $drivers; + if ($driver_note){ + $row[0]->{main::key($num++,0,4,'note')} = $driver_note; + } } } - if ($b_admin){ + if (!$show{'graphic-basic'} && $extra > 1 && $graphics{'display-rect'}){ + $row[0]->{main::key($num++,0,2,'d-rect')} = $graphics{'display-rect'}; + } + if (!$show{'graphic-basic'} && $extra > 1){ if (defined $graphics{'display-id'}){ $row[0]->{main::key($num++,0,2,'display-ID')} = $graphics{'display-id'}; } @@ -13535,452 +13804,234 @@ sub display_output(){ $row[0]->{main::key($num++,0,2,'default screen')} = $graphics{'display-default-screen'}; } } - if ($graphics{'no-xdpyinfo'}){ - $row[0]->{main::key($num++,0,2,'resolution')} = $graphics{'no-xdpyinfo'}; + if ($graphics{'no-screens'}){ + my $res = (!$show{'graphic-basic'} && $extra > 1 && !$graphics{'xvesa'}) ? 'note' : 'resolution'; + $row[0]->{main::key($num++,0,2,$res)} = $graphics{'no-screens'}; } elsif ($graphics{'screens'}){ my ($diag,$dpi,$hz,$size); - my ($m_count,$basic_count,$row_key,$screen_count) = (0,0,0,0); + my ($m_count,$basic_count,$screen_count) = (0,0,0); my $s_count = ($graphics{'screens'}) ? scalar @{$graphics{'screens'}}: 0; foreach my $main (@{$graphics{'screens'}}){ - $m_count = scalar @{$main->{'monitors'}} if $main->{'monitors'}; + $m_count = scalar keys %{$main->{'monitors'}} if $main->{'monitors'}; $screen_count++; - ($diag,$dpi,$hz,$resolution,$size) = (undef); - $row_key++ if !$show{'graphic-basic'}; + ($diag,$dpi,$hz,$resolution,$size) = (); + $j++ if !$show{'graphic-basic'}; if (!$show{'graphic-basic'} || $m_count == 0){ if (!$show{'graphic-basic'} && defined $main->{'screen'}){ - $row[$row_key]->{main::key($num++,1,2,'Screen')} = $main->{'screen'}; + $row[$j]->{main::key($num++,1,2,'Screen')} = $main->{'screen'}; + } + if ($main->{'res-x'} && $main->{'res-y'}){ + $resolution = $main->{'res-x'} . 'x' . $main->{'res-y'}; + $resolution .= '~' . $main->{'hz'} . 'Hz' if $show{'graphic-basic'}; } - $resolution = $main->{'res-x'} . 'x' . $main->{'res-y'} if $main->{'res-x'} && $main->{'res-y'}; - $resolution .= '~' . $main->{'hz'} . 'Hz' if $show{'graphic-basic'} && $main->{'hz'} && $resolution; $resolution ||= 'N/A'; if ($s_count == 1 || !$show{'graphic-basic'}){ - $row[$row_key]->{main::key($num++,0,3,'s-res')} = $resolution; + $row[$j]->{main::key($num++,0,3,'s-res')} = $resolution; } elsif ($show{'graphic-basic'}){ - $row[$row_key]->{main::key($num++,0,3,'s-res')} = '' if $screen_count == 1; - $row[$row_key]->{main::key($num++,0,3,$screen_count)} = $resolution; + $row[$j]->{main::key($num++,0,3,'s-res')} = '' if $screen_count == 1; + $row[$j]->{main::key($num++,0,3,$screen_count)} = $resolution; } - $resolution = ''; - if ($main->{'s-dpi'} && (!$show{'graphic-basic'} || $extra > 1)){ - $row[$row_key]->{main::key($num++,0,3,'s-dpi')} = $main->{'s-dpi'}; + if ($main->{'s-dpi'} && (!$show{'graphic-basic'} && $extra > 1)){ + $row[$j]->{main::key($num++,0,3,'s-dpi')} = $main->{'s-dpi'}; } - if (!$show{'graphic-basic'}){ - if ($main->{'size-x'} && $main->{'size-y'}){ - $size = $main->{'size-x'} . 'x' . $main->{'size-y'} . - 'mm ('. $main->{'size-x-i'} . 'x' . $main->{'size-y-i'} . '")'; + if (!$show{'graphic-basic'} && $extra > 2){ + if ($main->{'size-missing'}){ + $row[$j]->{main::key($num++,0,3,'s-size')} = $main->{'size-missing'}; } - $size ||= ''; - $row[$row_key]->{main::key($num++,0,3,'s-size')} = $size if $size; - if ($main->{'diagonal'}){ - $diag = $main->{'diagonal-m'} . 'mm ('. $main->{'diagonal'} . '")'; + else { + if ($main->{'size-x'} && $main->{'size-y'}){ + $size = $main->{'size-x'} . 'x' . $main->{'size-y'} . + 'mm ('. $main->{'size-x-i'} . 'x' . $main->{'size-y-i'} . '")'; + $row[$j]->{main::key($num++,0,3,'s-size')} = $size; + } + if ($main->{'diagonal'}){ + $diag = $main->{'diagonal-m'} . 'mm ('. $main->{'diagonal'} . '")'; + $row[$j]->{main::key($num++,0,3,'s-diag')} = $diag; + } } - $diag ||= ''; - $row[$row_key]->{main::key($num++,0,3,'s-diag')} = $diag if $diag; } } if ($main->{'monitors'}){ # print $basic_count . '::' . $m_count, "\n"; - foreach my $monitor (@{$main->{'monitors'}}){ - ($diag,$dpi,$hz,$resolution,$size) = (undef); - if ($show{'graphic-basic'}){ - $basic_count++; - if ($monitor->{'res-x'} && $monitor->{'res-y'}){ - $resolution = $monitor->{'res-x'} . 'x' . $monitor->{'res-y'}; - } - # using main, noit monitor, dpi because we want xorg dpi, not physical screen dpi - $dpi = $main->{'s-dpi'} if $resolution && $extra > 1 && $main->{'s-dpi'}; - $resolution .= '~' . $monitor->{'hz'} . 'Hz' if $monitor->{'hz'} && $resolution; - $resolution ||= 'N/A'; - if ($basic_count == 1 && $m_count == 1){ - $row[$row_key]->{main::key($num++,0,2,'resolution')} = $resolution; - } - else { - $row[$row_key]->{main::key($num++,1,2,'resolution')} = '' if $basic_count == 1; - $row[$row_key]->{main::key($num++,0,3,$basic_count)} = $resolution; - } - if ($m_count == $basic_count){ - $row[$row_key]->{main::key($num++,0,2,'s-dpi')} = $dpi if $dpi; - } - next; - } - $row_key++; - $row[$row_key]->{main::key($num++,0,3,'Monitor')} = $monitor->{'monitor'}; - if ($monitor->{'res-x'} && $monitor->{'res-y'}){ - $resolution = $monitor->{'res-x'} . 'x' . $monitor->{'res-y'}; - } - $resolution ||= 'N/A'; - $row[$row_key]->{main::key($num++,0,4,'res')} = $resolution; - $hz = ($monitor->{'hz'}) ? $monitor->{'hz'} : ''; - $row[$row_key]->{main::key($num++,0,4,'hz')} = $hz if $hz; - $dpi = ($monitor->{'dpi'}) ? $monitor->{'dpi'} : ''; - $row[$row_key]->{main::key($num++,0,4,'dpi')} = $dpi if $dpi; - # print "$dpi :: $main->{'s-dpi'}\n"; - if ($monitor->{'size-x'} && $monitor->{'size-y'}){ - $size = $monitor->{'size-x'} . 'x' . $monitor->{'size-y'} . - 'mm ('. $monitor->{'size-x-i'} . 'x' . $monitor->{'size-y-i'} . '")'; - } - $size ||= ''; - $row[$row_key]->{main::key($num++,0,4,'size')} = $size if $size; - if ($monitor->{'diagonal'}){ - $diag = $monitor->{'diagonal-m'} . 'mm ('. $monitor->{'diagonal'} . '")'; - } - $diag ||= ''; - $row[$row_key]->{main::key($num++,0,4,'diag')} = $diag if $diag; + $b_screen_monitors = 1; + if ($show{'graphic-basic'}){ + monitors_output_basic('screen',$main->{'monitors'}, + $main->{'s-dpi'},$j,\@row,\$num); } + else { + monitors_output_full('screen',$main->{'monitors'}, + $j,\@row,\$num); + } + } + elsif (!$show{'graphic-basic'} && $graphics{'no-monitors'}){ + $row[$j]->{main::key($num++,0,4,'monitors')} = $graphics{'no-monitors'}; } } } - else { + elsif (!$b_display){ $graphics{'tty'} ||= 'N/A'; $row[0]->{main::key($num++,0,2,'tty')} = $graphics{'tty'}; } + # fallback, if no xrandr/xdpyinfo, if wayland, if console. Note we've + # deleted each key used in advanced_monitor_data() so those won't show again + if (!$b_screen_monitors && $monitor_ids && %$monitor_ids){ + if ($show{'graphic-basic'}){ + monitors_output_basic('monitor',$monitor_ids,'',$j,\@row,\$num); + } + else { + monitors_output_full('monitor',$monitor_ids,$j,\@row,\$num); + } + } } eval $end if $b_log; return @row; } +sub monitors_output_basic { + eval $start if $b_log; + my ($type,$monitors,$s_dpi,$j,$row,$num) = @_; + my ($dpi,$resolution); + my ($basic_count,$m_count) = (0,scalar keys %{$monitors}); + foreach my $key (sort keys %{$monitors}){ + if ($type eq 'monitor' && (!$monitors->{$key}{'res-x'} || + !$monitors->{$key}{'res-y'})){ + next; + } + ($dpi,$resolution) = (); + $basic_count++; + if ($monitors->{$key}{'res-x'} && $monitors->{$key}{'res-y'}){ + $resolution = $monitors->{$key}{'res-x'} . 'x' . $monitors->{$key}{'res-y'}; + } + # using main, not monitor, dpi because we want xorg dpi, not physical screen dpi + $dpi = $s_dpi if $resolution && $extra > 1 && $s_dpi; + if ($monitors->{$key}{'hz'} && $resolution){ + $resolution .= '~' . $monitors->{$key}{'hz'} . 'Hz'; + } + $resolution ||= 'N/A'; + if ($basic_count == 1 && $m_count == 1){ + $row->[$j]{main::key($$num++,0,2,'resolution')} = $resolution; + } + else { + if ($basic_count == 1){ + $row->[$j]{main::key($$num++,1,2,'resolution')} = ''; + } + $row->[$j]{main::key($$num++,0,3,$basic_count)} = $resolution; + } + if (!$show{'graphic-basic'} && $m_count == $basic_count && $dpi){ + $row->[$j]{main::key($$num++,0,2,'s-dpi')} = $dpi; + } + } + eval $end if $b_log; +} +# row, num passed by ref +sub monitors_output_full { + eval $start if $b_log; + my ($type,$monitors,$j,$row,$num) = @_; + my ($b_no_size,$resolution); + my ($m1,$m2,$m3) = ($type eq 'screen') ? (3,4,5) : (2,3,4); + foreach my $key (sort keys %{$monitors}){ + $j++; + $$row[$j]->{main::key($$num++,1,$m1,'Monitor')} = $monitors->{$key}{'monitor'}; + if ($monitors->{$key}{'monitor-mapped'}){ + $$row[$j]->{main::key($$num++,0,$m2,'mapped')} = $monitors->{$key}{'monitor-mapped'}; + } + if ($monitors->{$key}{'position'}){ + $$row[$j]->{main::key($$num++,0,$m2,'pos')} = $monitors->{$key}{'position'}; + } + if ($monitors->{$key}{'model'}){ + $$row[$j]->{main::key($$num++,0,$m2,'model')} = $monitors->{$key}{'model'}; + } + if ($extra > 2 && $monitors->{$key}{'serial'}){ + $$row[$j]->{main::key($$num++,0,$m2,'serial')} = main::filter($monitors->{$key}{'serial'}); + } + if ($b_admin && $monitors->{$key}{'build-date'}){ + $$row[$j]->{main::key($$num++,0,$m2,'built')} = $monitors->{$key}{'build-date'}; + } + if ($monitors->{$key}{'res-x'} || $monitors->{$key}{'res-y'} || + $monitors->{$key}{'hz'} || $monitors->{$key}{'size-x'} || + $monitors->{$key}{'size-y'}){ + if ($monitors->{$key}{'res-x'} && $monitors->{$key}{'res-y'}){ + $resolution = $monitors->{$key}{'res-x'} . 'x' . $monitors->{$key}{'res-y'}; + } + $resolution ||= 'N/A'; + $$row[$j]->{main::key($$num++,0,$m2,'res')} = $resolution; + } + else { + if ($b_display){ + $resolution = main::message('monitor-na'); + } + else { + $resolution = main::message('monitor-console'); + } + $b_no_size = 1; + $$row[$j]->{main::key($$num++,0,$m2,'size-res')} = $resolution; + } + if ($extra > 2 && $monitors->{$key}{'hz'}){ + $$row[$j]->{main::key($$num++,0,$m2,'hz')} = $monitors->{$key}{'hz'}; + } + if ($monitors->{$key}{'dpi'}){ + $$row[$j]->{main::key($$num++,0,$m2,'dpi')} = $monitors->{$key}{'dpi'}; + } + if ($b_admin && $monitors->{$key}{'gamma'}){ + $$row[$j]->{main::key($$num++,0,$m2,'gamma')} = $monitors->{$key}{'gamma'}; + } + if ($extra > 2 && $monitors->{$key}{'scale'}){ + $$row[$j]->{main::key($$num++,0,$m2,'scale')} = $monitors->{$key}{'scale'}; + } + if ($extra > 2 && $monitors->{$key}{'size-x'} && $monitors->{$key}{'size-y'}){ + my $size = $monitors->{$key}{'size-x'} . 'x' . $monitors->{$key}{'size-y'} . + 'mm ('. $monitors->{$key}{'size-x-i'} . 'x' . $monitors->{$key}{'size-y-i'} . '")'; + $$row[$j]->{main::key($$num++,0,$m2,'size')} = $size; + } + if ($monitors->{$key}{'diagonal'}){ + my $diag = $monitors->{$key}{'diagonal-m'} . 'mm ('. $monitors->{$key}{'diagonal'} . '")'; + $$row[$j]->{main::key($$num++,0,$m2,'diag')} = $diag; + } + elsif ($b_display && !$b_no_size && !$monitors->{$key}{'size-x'} && + !$monitors->{$key}{'size-y'}){ + $$row[$j]->{main::key($$num++,0,$m2,'size')} = main::message('monitor-na');; + } + if ($b_admin && $monitors->{$key}{'ratio'}){ + $$row[$j]->{main::key($$num++,0,$m2,'ratio')} = $monitors->{$key}{'ratio'}; + } + if ($extra > 2){ + if ($monitors->{$key}{'modes-min-max'}){ + $$row[$j]->{main::key($$num++,0,$m2,'modes')} = $monitors->{$key}{'modes-min-max'}; + } + elsif ($monitors->{$key}{'modes-min'} && $monitors->{$key}{'modes-max'}){ + $$row[$j]->{main::key($$num++,1,$m2,'modes')} = ''; + $$row[$j]->{main::key($$num++,0,$m3,'max')} = $monitors->{$key}{'modes-max'}; + $$row[$j]->{main::key($$num++,0,$m3,'min')} = $monitors->{$key}{'modes-min'}; + } + } + } + # we only want to see gpu drivers for wayland since otherwise it's x drivers. +# if ($b_display && $b_admin && $graphics{'protocol'} && +# $graphics{'protocol'} eq 'wayland' && $monitors->{$key}{'drivers'}){ +# $driver = join(',',@{$monitors->{$key}{'drivers'}}); +# $$row[$j]->{main::key($$num++,0,$m2,'driver')} = $driver; +# } + eval $end if $b_log; +} -sub display_data_x { - eval $start if $b_log; - # X vendor and version detection. - # new method added since radeon and X.org and the disappearance of - # version : ...etc. Later on, the normal textual version string - # returned, e.g. like: X.Org version: 6.8.2 - # A failover mechanism is in place: if $version empty, release number parsed instead - if (my $program = main::check_program('xdpyinfo')){ - my ($diagonal,$diagonal_m,$dpi) = ('','',''); - my ($screen_id,$screen,@working); - my ($res_x,$res_x_i,$res_y,$res_y_i,$size_x,$size_x_i,$size_y,$size_y_i); - my @xdpyinfo = main::grabber("$program $display_opt 2>/dev/null","\n",'strip'); - # @xdpyinfo = map {s/^\s+//;$_} @xdpyinfo if @xdpyinfo; - # print join("\n",@xdpyinfo), "\n"; - foreach (@xdpyinfo){ - @working = split(/:\s+/, $_); - next if (($graphics{'screens'} && $working[0] !~ /^(dimensions$|screen\s#)/) || !$working[0]); - # print "$_\n"; - if ($working[0] eq 'vendor string'){ - $working[1] =~ s/The\s|\sFoundation//g; - # some distros, like fedora, report themselves as the xorg vendor, - # so quick check here to make sure the vendor string includes Xorg in string - if ($working[1] !~ /x/i){ - $working[1] .= ' X.org'; - } - $graphics{'x-vendor'} = $working[1]; - } - elsif ($working[0] eq 'name of display'){ - $graphics{'display-id'} = $working[1]; - } - elsif ($working[0] eq 'version number'){ - $graphics{'x-version-id'} = $working[1]; - } - # note used, fix that - elsif ($working[0] eq 'vendor release number'){ - $graphics{'x-vendor-release'} = $working[1]; - } - elsif ($working[0] eq 'X.Org version'){ - $graphics{'xorg-version'} = $working[1]; - } - elsif ($working[0] eq 'default screen number'){ - $graphics{'display-default-screen'} = $working[1]; - } - elsif ($working[0] eq 'number of screens'){ - $graphics{'display-screens'} = $working[1]; - } - elsif ($working[0] =~ /^screen #([0-9]+):/){ - $screen_id = $1; - $graphics{'screens'} = () if !$graphics{'screens'}; - } - elsif ($working[0] eq 'resolution'){ - $working[1] =~ s/^([0-9]+)x/$1/; - $graphics{'s-dpi'} = $working[1]; - } - elsif ($working[0] eq 'dimensions'){ - ($dpi,$res_x,$res_y,$size_x,$size_y) = (undef,undef,undef,undef,undef); - if ($working[1] =~ /([0-9]+)\s*x\s*([0-9]+)\s+pixels\s+\(([0-9]+)\s*x\s*([0-9]+)\s*millimeters\)/){ - $res_x = $1; - $res_y = $2; - $size_x = $3; - $size_y = $4; - $res_x_i = ($1) ? sprintf("%.1f", ($1/25.4)) : 0; - $res_y_i = ($2) ? sprintf("%.1f", ($2/25.4)) : 0; - $size_x_i = ($3) ? sprintf("%.1f", ($3/25.4)) : 0; - $size_y_i = ($4) ? sprintf("%.1f", ($4/25.4)) : 0; - $dpi = ($res_x && $size_x) ? sprintf("%.0f", ($res_x*25.4/$size_x)) : ''; - $diagonal = ($res_x && $size_x) ? sprintf("%.1f", (sqrt($size_x**2 + $size_y**2)/25.4)) : ''; - $diagonal += 0 if $diagonal;# trick to get rid of decimal 0 - $diagonal_m = ($res_x && $size_x) ? sprintf("%.0f", (sqrt($size_x**2 + $size_y**2))) : ''; - } - $screen = { - 'screen' => $screen_id, - 'res-x' => $res_x, - 'res-x-i' => $res_x_i, - 'res-y' => $res_y, - 'res-y-i' => $res_y_i, - 'size-x' => $size_x, - 'size-x-i' => $size_x_i, - 'size-y' => $size_y, - 'size-y-i' => $size_y_i, - 's-dpi' => $dpi, - 'diagonal' => $diagonal, - 'diagonal-m' => $diagonal_m, - }; - push(@{$graphics{'screens'}}, $screen); - } - } - # print Data::Dumper::Dumper $graphics{'screens'}; - if (my $program = main::check_program('xrandr')){ - ($diagonal,$diagonal_m,$dpi) = (undef); - ($screen_id,$screen,@working) = (undef); - ($res_x,$res_x_i,$res_y,$res_y_i,$size_x,$size_x_i,$size_y,$size_y_i) = (undef); - my (@monitors,$monitor_id,$screen,$screen_id,@xrandr_screens); - my @xrandr = main::grabber("$program $display_opt 2>/dev/null",'','strip'); - # $graphics{'dimensions'} = (\@dimensions); - # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle - # multiple screens from different video cards - foreach (@xrandr){ - if (/^Screen ([0-9]+):/){ - $screen_id = $1; - push(@xrandr_screens, \@monitors) if @monitors; - @monitors = (); - } - if (/^([^\s]+)\s+connected\s(primary\s)?([0-9]+)\s*x\s*([0-9]+)\+[0-9+]+(\s\([^)]+\))?(\s([0-9]+)mm\sx\s([0-9]+)mm)?/){ - $monitor_id = $1; - $res_x = $3; - $res_y = $4; - $size_x = $7; - $size_y = $8; - $res_x_i = ($3) ? sprintf("%.1f", ($3/25.4)) : 0; - $res_y_i = ($4) ? sprintf("%.1f", ($4/25.4)) : 0; - $size_x_i = ($7) ? sprintf("%.1f", ($7/25.4)) : 0; - $size_y_i = ($8) ? sprintf("%.1f", ($8/25.4)) : 0; - $dpi = ($res_x && $size_x) ? sprintf("%.0f", $res_x * 25.4 / $size_x) : ''; - $diagonal = ($res_x && $size_x) ? sprintf("%.1f", (sqrt($size_x**2 + $size_y**2)/25.4)) : ''; - $diagonal += 0 if $diagonal; # trick to get rid of decimal 0 - $diagonal_m = ($res_x && $size_x) ? sprintf("%.0f", (sqrt($size_x**2 + $size_y**2))) : ''; - push(@monitors, { - 'screen' => $screen_id, - 'monitor' => $monitor_id, - 'res-x' => $res_x, - 'res-x-i' => $res_x_i, - 'res-y' => $res_y, - 'res-y-i' => $res_y_i, - 'size-x' => $size_x, - 'size-x-i' => $size_x_i, - 'size-y' => $size_y, - 'size-y-i' => $size_y_i, - 'dpi' => $dpi, - 'diagonal' => $diagonal, - 'diagonal-m' => $diagonal_m, - }); - # print "x:$size_x y:$size_y rx:$res_x ry:$res_y dpi:$dpi\n"; - ($res_x,$res_x_i,$res_y,$res_y_i,$size_x,$size_x_i,$size_y,$size_y_i) = (0,0,0,0,0,0,0,0); - - } - my @working = split(/\s+/,$_); - if ($working[1] =~ /\*/){ - $working[1] =~ s/\*|\+//g; - $working[1] = sprintf("%.0f",$working[1]); - $monitors[scalar @monitors - 1]->{'hz'} = $working[1] if @monitors; - ($diagonal,$dpi) = ('',''); - # print Data::Dumper::Dumper \@monitors; - } - } - push(@xrandr_screens, \@monitors) if @monitors; - # print "xrand: " . Data::Dumper::Dumper \@xrandr_screens; - my ($i) = (0); - foreach my $main (@{$graphics{'screens'}}){ - # print "h: " . Data::Dumper::Dumper $main; - # print $main->{'screen'}, "\n"; - foreach my $screens (@xrandr_screens){ - # print "d: " . Data::Dumper::Dumper $screens; - if ($screens->[0]{'screen'} eq $main->{'screen'}){ - $graphics{'screens'}->[$i]{'monitors'} = $screens; - last; - } - } - $i++; - } - if (!$graphics{'screens'}){ - $graphics{'tty'} = tty_data(); - } - } - } - else { - $graphics{'no-xdpyinfo'} = main::message('tool-missing-basic','xdpyinfo'); - } - print 'last: ', Data::Dumper::Dumper $graphics{'screens'} if $dbg[17]; - main::log_data('dump','$graphics{screens}',$graphics{'screens'}) if $b_log; - eval $end if $b_log; -} -sub display_data_wayland { - eval $start if $b_log; - if ($ENV{'WAYLAND_DISPLAY'}){ - $graphics{'display-id'} = $ENV{'WAYLAND_DISPLAY'}; - # return as wayland-0 or 0? - $graphics{'display-id'} =~ s/wayland-?//i; - } - # print 'last: ', Data::Dumper::Dumper $graphics{'screens'} if $dbg[17]; - # main::log_data('dump','@graphics{screens}',$graphics{'screens'}) if $b_log; - eval $end if $b_log; -} -sub set_compositor { - eval $start if $b_log; - my ($protocol) = @_; - # initial tests, if wayland, it is certainly a compositor - $protocol = lc($protocol) if $protocol; - $graphics{'compositor'} = display_compositor($protocol); - # gnome-shell is incredibly slow to return version - if (($extra > 2 || $protocol eq 'wayland') && $graphics{'compositor'} && - (!$show{'system'} || $graphics{'compositor'} ne 'gnome-shell')){ - $graphics{'compositor-version'} = (main::program_data($graphics{'compositor'},$graphics{'compositor'},3))[1]; - } - eval $end if $b_log; -} -sub get_protocol { - eval $start if $b_log; - my ($protocol) = (''); - $protocol = $ENV{'XDG_SESSION_TYPE'} if $ENV{'XDG_SESSION_TYPE'}; - $protocol = $ENV{'WAYLAND_DISPLAY'} if (!$protocol && $ENV{'WAYLAND_DISPLAY'}); - # can show as wayland-0 - $protocol = 'wayland' if $protocol && $protocol =~ /wayland/i; - # yes, I've seen this in 2019 distros, sigh - $protocol = '' if $protocol eq 'tty'; - # need to confirm that there's a point to this test, I believe no, fails out of x - # loginctl also results in the session id - if (!$protocol && $b_display && $force{'display'}){ - if (my $program = main::check_program('loginctl')){ - my $id = ''; - # $id = $ENV{'XDG_SESSION_ID'}; # returns tty session in console - my @data = main::grabber("$program --no-pager --no-legend 2>/dev/null",'','strip'); - foreach (@data){ - next if /tty[v]?[0-6]$/; # freebsd: ttyv3 - $id = (split(/\s+/, $_))[0]; - last; # multiuser? too bad, we'll go for the first one - } - if ($id){ - my $temp = (main::grabber("$program show-session $id -p Type --no-pager --no-legend 2>/dev/null"))[0]; - $temp =~ s/Type=// if $temp; - # ssh will not show /dev/ttyx so would have passed the first test - $protocol = $temp if $temp && $temp ne 'tty'; - } - } - } - eval $end if $b_log; - return $protocol; -} -sub gl_output(){ +## INTERFACE OUTPUT ## +# as soon as EGL for Wayland appears add it! +sub interface_output { eval $start if $b_log; my $num = 0; - my (@row,$arg); + my (@row,$program,$type); # print ("$b_display : $b_root\n"); if ($b_display){ - if (my $program = main::check_program('glxinfo')){ - # NOTE: glxinfo -B is not always available, unfortunately - my @glxinfo = main::grabber("$program $display_opt 2>/dev/null"); - # my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/graphics/glxinfo/glxinfo-ssh-centos.txt"; - # my @glxinfo = main::reader($file); - if (!@glxinfo){ - my $type = 'display-console'; - if ($b_root){ - $type = 'display-root-x'; - } - else { - $type = 'display-null'; - } - @row = ({ - main::key($num++,0,1,'Message') => main::message($type), - }); - return @row; - } - # print join("\n", @glxinfo),"\n"; - my $compat_version = ''; - my ($b_compat,$b_nogl,@core_profile_version,@direct_render,@renderer, - @opengl_version,@working); - foreach (@glxinfo){ - next if /^\s/; - if (/^opengl renderer/i){ - @working = split(/:\s*/, $_, 2); - if ($working[1]){ - $working[1] = main::clean($working[1]); - # Allow all mesas - # if ($working[1] =~ /mesa/i){ - # - # } - } - # note: there are cases where gl drivers are missing and empty - # field value occurs. - else { - $b_nogl = 1; - $working[1] = main::message('gl-empty'); - } - push(@renderer, $working[1]); - } - # dropping all conditions from this test to just show full mesa information - # there is a user case where not f and mesa apply, atom mobo - # /opengl version/ && ( f || $2 !~ /mesa/){ - elsif (/^opengl version/i){ - @working = split(/:\s*/, $_, 2); - if ($working[1]){ - # fglrx started appearing with this extra string, does not appear - # to communicate anything of value - $working[1] =~ s/(Compatibility Profile Context|\(Compatibility Profile\))//; - $working[1] =~ s/\s\s/ /g; - $working[1] =~ s/^\s+|\s+$//; - push(@opengl_version, $working[1]); - # note: this is going to be off if ever multi opengl versions appear, - # never seen one - @working = split(/\s+/, $working[1]); - $compat_version = $working[0]; - } - elsif (!$b_nogl){ - push(@opengl_version, main::message('gl-empty')); - } - } - elsif (/^opengl core profile version/i){ - @working = split(/:\s*/, $_, 2); - # note: no need to apply empty message here since we don't have the data - # anyway - if ($working[1]){ - # fglrx started appearing with this extra string, does not appear - # to communicate anything of value - $working[1] =~ s/(Compatibility Profile Context|\((Compatibility|Core) Profile\))//; - $working[1] =~ s/\s\s/ /g; - $working[1] =~ s/^\s+|\s+$//; - push(@core_profile_version, $working[1]); - } - } - elsif (/direct rendering/){ - @working = split(/:\s*/, $_, 2); - push(@direct_render, $working[1]); - } - # if -B was always available, we could skip this, but it is not - elsif (/GLX Visuals/){ - last; - } - } - my ($direct_render,$renderer,$version) = ('N/A','N/A','N/A'); - $direct_render = join(', ', @direct_render) if @direct_render; - # non free drivers once filtered and cleaned show the same for core and compat - # but this stopped for some reason at 4.5/4.6 nvidia - if (@core_profile_version && @opengl_version && - join('', @core_profile_version) ne join('', @opengl_version) && - !(grep {/nvidia/i} @opengl_version)){ - @opengl_version = @core_profile_version; - $b_compat = 1; - } - $version = join(', ', @opengl_version) if @opengl_version; - $renderer = join(', ', @renderer) if @renderer; - @row = ({ - main::key($num++,1,1,'OpenGL') => '', - main::key($num++,1,2,'renderer') => ($renderer) ? $renderer : 'N/A', - main::key($num++,0,2,'v') => ($version) ? $version : 'N/A', - }); - if ($b_compat && $extra > 1 && $compat_version){ - $row[0]->{main::key($num++,0,2,'compat-v')} = $compat_version; - } - if ($extra > 0){ - $row[0]->{main::key($num++,0,2,'direct render')} = $direct_render; - } + if (!$force{'wayland'} && ($program = main::check_program('glxinfo'))){ + interface_glx_output($program,\@row,\$num); + } + elsif ($graphics{'xvesa'}){ + interface_vesa_output(\@row,\$num); + } + # handles no data until we find one for wayland egl data + elsif ($graphics{'protocol'} eq 'wayland'){ + interface_egl_output(\@row,\$num); } else { @row = ({ @@ -13989,16 +14040,27 @@ sub gl_output(){ } } else { - my $type = 'display-console'; - if (!main::check_program('glxinfo')){ - $type = 'glxinfo-missing'; + $type = 'gl-console'; + if ($graphics{'xvesa'}){ + interface_vesa_output(\@row,\$num); } - else { - if ($b_root){ - $type = 'display-root'; + elsif (!main::check_program('glxinfo')){ + if ($graphics{'protocol'} eq 'wayland'){ + $type = 'interface-wayland'; } else { - $type = 'display-try'; + $type = 'glxinfo-missing'; + } + } + else { + if ($graphics{'protocol'} eq 'wayland'){ + $type = 'interface-wayland'; + } + elsif ($b_root){ + $type = 'gl-root'; + } + else { + $type = 'gl-try'; } } @row = ({ @@ -14008,31 +14070,968 @@ sub gl_output(){ eval $end if $b_log; return @row; } -sub tty_data(){ +sub interface_egl_output { eval $start if $b_log; - my ($tty); - if ($size{'term-cols'}){ - $tty = "$size{'term-cols'}x$size{'term-lines'}"; + my ($row,$num) = @_; + @$row = ({ + main::key($num++,0,1,'Message') => main::message('interface-wayland'), + }); + eval $end if $b_log; +} +sub interface_glx_output { + eval $start if $b_log; + my ($program,$row,$num) = @_; + # NOTE: glxinfo -B is not always available, unfortunately + my @glxinfo = main::grabber("$program $display_opt 2>/dev/null"); + # my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/graphics/glxinfo/glxinfo-ssh-centos.txt"; + # my @glxinfo = main::reader($file); + if (!@glxinfo){ + my $type = 'gl-console'; + if ($b_root){ + $type = 'gl-root-display'; + } + else { + $type = 'gl-null'; + } + @$row = ({ + main::key($$num++,0,1,'Message') => main::message($type), + }); + return; } - # this is broken - elsif ($b_irc && $client{'console-irc'}){ - ShellData::console_irc_tty() if !$loaded{'con-irc-tty'}; - my $tty_working = $client{'con-irc-tty'}; - if ($tty_working ne '' && (my $program = main::check_program('stty'))){ - my $tty_arg = ($bsd_type) ? '-f' : '-F'; - # handle vtnr integers, and tty ID with letters etc. - $tty_working = "tty$tty_working" if -e "/dev/tty$tty_working"; - $tty = (main::grabber("$program $tty_arg /dev/$tty_working size 2>/dev/null"))[0]; - if ($tty){ - my @temp = split(/\s+/, $tty); - $tty = "$temp[1]x$temp[0]"; + # print join("\n", @glxinfo),"\n"; + my $compat_version = ''; + my ($b_compat,$b_nogl,@core_profile_version,@direct_render,@renderer, + @opengl_version,@working); + foreach (@glxinfo){ + next if /^\s/; + if (/^opengl renderer/i){ + @working = split(/:\s*/, $_, 2); + if ($working[1]){ + $working[1] = main::clean($working[1]); } + # note: there are cases where gl drivers are missing and empty + # field value occurs. + else { + $b_nogl = 1; + $working[1] = main::message('gl-empty'); + } + push(@renderer, $working[1]); + } + # dropping all conditions from this test to just show full mesa information + # there is a user case where not f and mesa apply, atom mobo + # /opengl version/ && ( f || $2 !~ /mesa/){ + elsif (/^opengl version/i){ + @working = split(/:\s*/, $_, 2); + if ($working[1]){ + # fglrx started appearing with this extra string, does not appear + # to communicate anything of value + $working[1] =~ s/(Compatibility Profile Context|\(Compatibility Profile\))//; + $working[1] =~ s/\s\s/ /g; + $working[1] =~ s/^\s+|\s+$//; + push(@opengl_version, $working[1]); + # note: this is going to be off if ever multi opengl versions appear, + # never seen one + @working = split(/\s+/, $working[1]); + $compat_version = $working[0]; + } + elsif (!$b_nogl){ + push(@opengl_version, main::message('gl-empty')); + } + } + elsif (/^opengl core profile version/i){ + @working = split(/:\s*/, $_, 2); + # note: no need to apply empty message here since we don't have the data + # anyway + if ($working[1]){ + # fglrx started appearing with this extra string, does not appear + # to communicate anything of value + $working[1] =~ s/(Compatibility Profile Context|\((Compatibility|Core) Profile\))//; + $working[1] =~ s/\s\s/ /g; + $working[1] =~ s/^\s+|\s+$//; + push(@core_profile_version, $working[1]); + } + } + elsif (/direct rendering/){ + @working = split(/:\s*/, $_, 2); + push(@direct_render, $working[1]); + } + # if -B was always available, we could skip this, but it is not + elsif (/GLX Visuals/){ + last; + } + } + my ($direct_render,$renderer,$version) = ('N/A','N/A','N/A'); + $direct_render = join(', ', @direct_render) if @direct_render; + # non free drivers once filtered and cleaned show the same for core and compat + # but this stopped for some reason at 4.5/4.6 nvidia + if (@core_profile_version && @opengl_version && + join('', @core_profile_version) ne join('', @opengl_version) && + !(grep {/nvidia/i} @opengl_version)){ + @opengl_version = @core_profile_version; + $b_compat = 1; + } + $version = join(', ', @opengl_version) if @opengl_version; + $renderer = join(', ', @renderer) if @renderer; + @$row = ({ + main::key($$num++,1,1,'OpenGL') => '', + main::key($$num++,1,2,'renderer') => ($renderer) ? $renderer : 'N/A', + main::key($$num++,0,2,'v') => ($version) ? $version : 'N/A', + }); + if ($b_compat && $extra > 1 && $compat_version){ + $row->[0]{main::key($$num++,0,2,'compat-v')} = $compat_version; + } + if ($extra > 0){ + $row->[0]{main::key($$num++,0,2,'direct render')} = $direct_render; + } + eval $end if $b_log; +} +# WARNING! Never seen a GOP type UEFI, needs more data +sub interface_vesa_output { + eval $start if $b_log; + my ($row,$num) = @_; + my ($controller,$dac,$interface,$ram,$source,$version); + # note: goes to stderr, not stdout + my @data = main::grabber($graphics{'xvesa'} . ' -listmodes 2>&1'); + if ($data[0] && $data[0] =~ /^(VBE|GOP)\s+version\s+(\S+)\s\(([^)]+)\)/i){ + $interface = $1; + $version = $2; + $source = $3; + } + if ($data[1] && $data[1] =~ /^DAC is ([^,]+), controller is ([^,]+)/i){ + $dac = $1; + $controller = $2; + } + if ($data[2] && $data[2] =~ /^Total memory:\s+(\d+)\s/i){ + $ram = $1; + $ram = main::get_size($ram,'string'); + } + if (!$interface){ + $row->[0]{main::key($$num++,0,1,'Message')} = main::message('xvesa-interface'); + } + else { + $row->[0]{main::key($$num++,1,1,'Interface')} = $interface; + $row->[0]{main::key($$num++,0,2,'v')} = ($version) ? $version : 'N/A'; + $row->[0]{main::key($$num++,0,2,'source')} = ($source) ? $source : 'N/A'; + if ($dac){ + $row->[0]{main::key($$num++,0,2,'dac')} = $dac; + $row->[0]{main::key($$num++,0,2,'controller')} = $controller; + } + if ($ram){ + $row->[0]{main::key($$num++,0,2,'ram')} = $ram; } } eval $end if $b_log; - return $tty; } -sub x_drivers { + +## DISPLAY DATA WAYLAND ## +sub display_data_wayland { + eval $start if $b_log; + my ($b_skip_pos,$program); + if ($ENV{'WAYLAND_DISPLAY'}){ + $graphics{'display-id'} = $ENV{'WAYLAND_DISPLAY'}; + # return as wayland-0 or 0? + $graphics{'display-id'} =~ s/wayland-?//i; + } + if ($fake{'swaymsg'} || ($program = main::check_program('swaymsg'))){ + swaymsg_data($program); + } + # until we get data proving otherwise, assuming these have same output + elsif ($fake{'wl-info'} || (($program = main::check_program('wayland-info')) || + ($program = main::check_program('weston-info')))){ + wlinfo_data($program); + } + elsif ($fake{'wlr-randr'} || ($program = main::check_program('wlr-randr'))){ + wlrrandr_data($program); + } + # make sure we got enough for advanced position data, might be from /sys + if ($extra > 1 && $monitor_ids){ + $b_skip_pos = check_wayland_data(); + } + if ($extra > 1 && $monitor_ids && $b_wayland_data){ + # map_monitor_ids([keys %$monitors]); # not required, but leave in case. + wayland_data_advanced($b_skip_pos); + } + print 'Wayland monitors: ', Data::Dumper::Dumper $monitor_ids if $dbg[17]; + main::log_data('dump','$monitor_ids',$monitor_ids) if $b_log; + eval $end if $b_log; +} +# if we didn't get explicit tool for wayland data, check to see if we got most +# of the data from /sys/class/drm edid and then skip xrandr to avoid gunking up +# the data, in that case, all we get from xrandr would be the position, which is +# nice but not a must-have. We've already cleared out all disabled ports. +sub check_wayland_data { + eval $start if $b_log; + my ($b_skip_pos,$b_invalid); + foreach my $key (keys %$monitor_ids){ + # we need these 4 items to construct the grid rectangle + if (!defined $monitor_ids->{$key}{'pos-x'} || + !defined $monitor_ids->{$key}{'pos-y'} || + !$monitor_ids->{$key}{'res-x'} || !$monitor_ids->{$key}{'res-y'}){ + $b_skip_pos = 1; + } + if (!$monitor_ids->{$key}{'res-x'} || !$monitor_ids->{$key}{'res-y'}){ + $b_invalid = 1; + } + } + # ok, we have enough, we don't need to do fallback xrandr checks + $b_wayland_data = 1 if !$b_invalid; + eval $end if $b_log; + return $b_skip_pos; +} +# Set Display rect size for > 1 monitors, monitor positions, size-i, diag +sub wayland_data_advanced { + eval $start if $b_log; + my ($b_skip_pos) = @_; + my (%x_pos,%y_pos); + my ($x_max,$y_max) = (0,0); + my @keys = keys %$monitor_ids; + foreach my $key (@keys){ + if (!$b_skip_pos){ + if ($monitor_ids->{$key}{'res-x'} && $monitor_ids->{$key}{'res-x'} > $x_max){ + $x_max = $monitor_ids->{$key}{'res-x'}; + } + if ($monitor_ids->{$key}{'res-y'} && $monitor_ids->{$key}{'res-y'} > $y_max){ + $y_max = $monitor_ids->{$key}{'res-y'}; + } + # Now we'll add the detected x, y res to the trackers + if (!defined $x_pos{$monitor_ids->{$key}{'pos-x'}}){ + $x_pos{$monitor_ids->{$key}{'pos-x'}} = $monitor_ids->{$key}{'res-x'}; + } + if (!defined $y_pos{$monitor_ids->{$key}{'pos-y'}}){ + $y_pos{$monitor_ids->{$key}{'pos-y'}} += $monitor_ids->{$key}{'res-y'}; + } + } + # this means we failed to get EDID real data, and are using just the wayland + # tool to get this info, eg. with BSD without compositor data. + if ($monitor_ids->{$key}{'size-x'} && $monitor_ids->{$key}{'size-y'} && + (!$monitor_ids->{$key}{'size-x-i'} || !$monitor_ids->{$key}{'size-y-i'} || + !$monitor_ids->{$key}{'dpi'} || !$monitor_ids->{$key}{'diagonal'})){ + my $size_x = $monitor_ids->{$key}{'size-x'}; + my $size_y = $monitor_ids->{$key}{'size-y'}; + $monitor_ids->{$key}{'size-x-i'} = sprintf("%.1f", ($size_x/25.4)); + $monitor_ids->{$key}{'size-y-i'} = sprintf("%.1f", ($size_y/25.4)); + $monitor_ids->{$key}{'diagonal'} = sprintf("%.1f", (sqrt($size_x**2 + $size_y**2)/25.4)) + 0; + $monitor_ids->{$key}{'diagonal-m'} = sprintf("%.0f", (sqrt($size_x**2 + $size_y**2))); + if ($monitor_ids->{$key}{'res-x'}){ + my $res_x = $monitor_ids->{$key}{'res-x'}; + $monitor_ids->{$key}{'dpi'} = sprintf("%.0f", $res_x * 25.4 / $size_x); + } + } + } + if (!$b_skip_pos){ + if (scalar @keys > 1 && %x_pos && %y_pos){ + my ($x,$y) = (0,0); + foreach (keys %x_pos){$x += $x_pos{$_}} + foreach (keys %y_pos){$y += $y_pos{$_}} + # handle cases with one tall portrait mode > 2 short landscapes, etc. + $x = $x_max if $x_max > $x; + $y = $y_max if $y_max > $y; + $graphics{'display-rect'} = $x . 'x' . $y; + } + my $layouts = []; + set_monitor_layouts($layouts); + # only update position, we already have all the rest of the data + advanced_monitor_data($monitor_ids,$layouts); + undef $layouts; + } + eval $end if $b_log; +} + +## WAYLAND COMPOSITOR DATA TOOLS ## + +# NOTE: These patterns are VERY fragile, and depend on no changes at all to +# the data structure, and more important, the order. Something I would put +# almost no money on being able to count on. +sub wlinfo_data { + eval $start if $b_log; + my ($program) = @_; + my (@data,%mon,@temp,$ref); + my ($b_iwlo,$b_izxdg,$file,$hz,$id,$pos_x,$pos_y,$res_x,$res_y,$scale); + if (!$fake{'wl-info'}){ + undef $monitor_ids; + @data = main::grabber("$program 2>/dev/null",'','strip'); + } + else { + $file = "$ENV{'HOME'}/bin/scripts/inxi/data/wayland/weston-info-2-mon-1.txt"; + @data = main::reader($file,'strip'); + } + print 'wayland/weston-info raw: ', Data::Dumper::Dumper \@data if $dbg[46]; + main::log_data('dump','@data', \@data) if $b_log; + foreach (@data){ + # print 'l: ', $_,"\n"; + if (/^interface: 'wl_output', version: \d+, name: (\d+)$/){ + $b_iwlo = 1; + $id = $1; + } + elsif (/^interface: 'zxdg_output/){ + $b_izxdg = 1; + $b_iwlo = 0; + } + if ($b_iwlo){ + if (/^x: (\d+), y: (\d+), scale: ([\d\.]+)/){ + $mon{$id}->{'pos-x'} = $1; + $mon{$id}->{'pos-y'} = $2; + $mon{$id}->{'scale'} = $3; + } + elsif (/^physical_width: (\d+) mm, physical_height: (\d+) mm/){ + $mon{$id}->{'size-x'} = $1 if $1; # can be 0 if edid data n/a + $mon{$id}->{'size-y'} = $2 if $2; # can be 0 if edid data n/a + } + elsif (/^make: '([^']+)', model: '([^']+)'/){ + my $make = main::clean($1); + my $model = main::clean($2); + $mon{$id}->{'model'} = $make; + if ($make && $model){ + $mon{$id}->{'model'} = $make . ' ' . $model; + } + elsif ($model) { + $mon{$id}->{'model'} = $model; + } + elsif ($make) { + $mon{$id}->{'model'} = $make; + } + # includes remove duplicates and remove unset + if ($mon{$id}->{'model'}){ + $mon{$id}->{'model'} = main::clean_dmi($mon{$id}->{'model'}); + } + } + elsif (/^width: (\d+) px, height: (\d+) px, refresh: ([\d\.]+) Hz,/){ + $mon{$id}->{'res-x'} = $1; + $mon{$id}->{'res-y'} = $2; + $mon{$id}->{'hz'} = sprintf('%.0f',$3); + } + } + # note: we don't want to use the 'description' field because that doesn't + # always contain make/model data, sometimes it's: Built-in/Unknown Display + elsif ($b_izxdg){ + if (/^output: (\d+)/){ + $id = $1; + } + elsif (/^name: '([^']+)'$/){ + $mon{$id}->{'monitor'} = $1; + } + elsif (/^logical_x: (\d+), logical_y: (\d+)/){ + $mon{$id}->{'log-pos-x'} = $1; + $mon{$id}->{'log-pos-y'} = $2; + } + elsif (/^logical_width: (\d+), logical_height: (\d+)/){ + $mon{$id}->{'log-x'} = $1; + $mon{$id}->{'log-y'} = $2; + } + } + if ($b_izxdg && /^interface: '(?!zxdg_output)/){ + last; + } + } + # now we need to map %mon back to $monitor_ids + if (%mon){ + $b_wayland_data = 1; + foreach my $key (keys %mon){ + next if !$mon{$key}->{'monitor'}; # no way to know what it is, sorry + $id = $mon{$key}->{'monitor'}; + $monitor_ids->{$id}{'monitor'} = $id; + $monitor_ids->{$id}{'log-x'} = $mon{$key}->{'log-x'} if defined $mon{$key}->{'log-x'}; + $monitor_ids->{$id}{'log-y'} = $mon{$key}->{'log-y'} if defined $mon{$key}->{'log-y'}; + $monitor_ids->{$id}{'pos-x'} = $mon{$key}->{'pos-x'} if defined $mon{$key}->{'pos-x'}; + $monitor_ids->{$id}{'pos-y'} = $mon{$key}->{'pos-y'} if defined $mon{$key}->{'pos-y'}; + $monitor_ids->{$id}{'res-x'} = $mon{$key}->{'res-x'} if defined $mon{$key}->{'res-x'}; + $monitor_ids->{$id}{'res-y'} = $mon{$key}->{'res-y'} if defined $mon{$key}->{'res-y'}; + $monitor_ids->{$id}{'size-x'} = $mon{$key}->{'size-x'} if defined $mon{$key}->{'size-x'}; + $monitor_ids->{$id}{'size-y'} = $mon{$key}->{'size-y'} if defined $mon{$key}->{'size-y'}; + $monitor_ids->{$id}{'hz'} = $mon{$key}->{'hz'} if defined $mon{$key}->{'hz'}; + if (defined $mon{$key}->{'model'} && !$monitor_ids->{$id}{'model'}){ + $monitor_ids->{$id}{'model'} = $mon{$key}->{'model'}; + } + $monitor_ids->{$id}{'scale'} = $mon{$key}->{'scale'} if defined $mon{$key}->{'scale'}; + # fallbacks in case wl_output block is not present, which happens + if (!defined $mon{$key}->{'pos-x'} && defined $mon{$key}->{'log-pos-x'}){ + $monitor_ids->{$id}{'pos-x'} = $mon{$key}->{'log-pos-x'}; + } + if (!defined $mon{$key}->{'pos-y'} && defined $mon{$key}->{'log-pos-y'}){ + $monitor_ids->{$id}{'pos-y'} = $mon{$key}->{'log-pos-y'}; + } + if (!defined $mon{$key}->{'res-x'} && defined $mon{$key}->{'log-x'}){ + $monitor_ids->{$id}{'res-x'} = $mon{$key}->{'log-x'}; + } + if (!defined $mon{$key}->{'res-y'} && defined $mon{$key}->{'log-y'}){ + $monitor_ids->{$id}{'res-y'} = $mon{$key}->{'log-y'}; + } + } + } + print '%mon: ', Data::Dumper::Dumper \%mon if $dbg[46]; + main::log_data('dump','%mon', \%mon) if $b_log; + print 'wayland/weston-info: monitor_ids: ', Data::Dumper::Dumper $monitor_ids if $dbg[46]; + eval $end if $b_log; +} +# note; since not all systems will have /sys data, we'll repack it if it's +# missing here. +sub swaymsg_data { + eval $start if $b_log; + my ($program) = @_; + my (@data,%json,@temp,$ref); + my ($b_json,$file,$hz,$id,$model,$pos_x,$pos_y,$res_x,$res_y,$scale,$serial); + if (!$fake{'swaymsg'}){ + main::load_json() if !$loaded{'json'}; + if ($use{'json'}){ + my $result = qx($program -t get_outputs -r 2>/dev/null); + # returns array of monitors found + @data = &{$use{'json'}->{'decode'}}($result) if $result; + $b_json = 1; + print "$use{'json'}->{'type'}: " if $dbg[46]; + # print "using: $use{'json'}->{'type'}\n"; + } + else { + @data = main::grabber("$program -t get_outputs -p 2>/dev/null",'','strip'); + } + } + else { + undef $monitor_ids; + $file = "$ENV{'HOME'}/bin/scripts/inxi/data/wayland/swaymsg-2-monitor-1.txt"; + @data = main::reader($file,'strip'); + } + print 'swaymsg: ', Data::Dumper::Dumper \@data if $dbg[46]; + main::log_data('dump','@data', \@data) if $b_log; + # print Data::Dumper::Dumper \@data; + if ($b_json){ + $b_wayland_data = 1 if scalar @data > 0; + foreach my $display (@data){ + foreach my $mon (@$display){ + ($hz,$pos_x,$pos_y,$res_x,$res_y,$scale) = (); + $id = $mon->{'name'}; + if (!$monitor_ids->{$id}{'monitor'}){ + $monitor_ids->{$id}{'monitor'} = $mon->{'name'}; + } + # we don't want to overwrite good edid model data if we already got it + if (!$monitor_ids->{$id}{'model'} && $mon->{'make'}){ + $monitor_ids->{$id}{'model'} = main::clean($mon->{'make'}); + if ($mon->{'model'}){ + $monitor_ids->{$id}{'model'} .= ' ' . main::clean($mon->{'model'}); + + } + $monitor_ids->{$id}{'model'} = main::remove_duplicates($monitor_ids->{$id}{'model'}); + } + if ($monitor_ids->{$id}{'primary'} && + $monitor_ids->{$id}{'primary'} ne 'false'){ + $monitor_ids->{$id}{'primary'} = $id; + } + if (!$monitor_ids->{$id}{'serial'}){ + $monitor_ids->{$id}{'serial'} = main::clean_dmi($mon->{'serial'}); + } + # sys data will only have edid type info, not active state res/pos/hz + if ($mon->{'current_mode'}){ + if ($hz = $mon->{'current_mode'}{'refresh'}){ + $hz = sprintf('%.0f',($mon->{'current_mode'}{'refresh'}/1000)); + $monitor_ids->{$id}{'hz'} = $hz; + } + $monitor_ids->{$id}{'res-x'} = $mon->{'current_mode'}{'width'}; + $monitor_ids->{$id}{'res-y'} = $mon->{'current_mode'}{'height'}; + } + if ($mon->{'rect'}){ + $monitor_ids->{$id}{'pos-x'} = $mon->{'rect'}{'x'}; + $monitor_ids->{$id}{'pos-y'} = $mon->{'rect'}{'y'}; + } + if ($mon->{'scale'}){ + $monitor_ids->{$id}{'scale'} =$mon->{'scale'}; + } + } + } + } + else { + foreach (@data){ + push(@temp,'~~') if /^Output/i; + push(@temp,$_); + } + push(@temp,'~~') if @temp; + @data = @temp; + $b_wayland_data = 1 if scalar @data > 8; + foreach (@data){ + if ($_ eq '~~' && $id){ + $monitor_ids->{$id}{'hz'} = $hz; + $monitor_ids->{$id}{'model'} = $model if $model; + $monitor_ids->{$id}{'monitor'} = $id; + $monitor_ids->{$id}{'pos-x'} = $pos_x; + $monitor_ids->{$id}{'pos-y'} = $pos_y; + $monitor_ids->{$id}{'res-x'} = $res_x; + $monitor_ids->{$id}{'res-y'} = $res_y; + $monitor_ids->{$id}{'scale'} = $scale; + $monitor_ids->{$id}{'serial'} = $serial if $serial; + ($hz,$model,$pos_x,$pos_y,$res_x,$res_y,$scale,$serial) = (); + $b_wayland_data = 1; + } + # Output VGA-1 ' ' (focused) + # unknown how 'primary' is shown, if it shows in this output + if (/^Output (\S+) '([^']+)'/i){ + $id = $1; + if ($2 && !$monitor_ids->{$id}{'model'}){ + ($model,$serial) = get_model_serial($2); + } + } + elsif (/^Current mode:\s+(\d+)x(\d+)\s+\@\s+([\d\.]+)\s+Hz/i){ + $res_x = $1; + $res_y = $2; + $hz = (sprintf('%.0f',($3/1000)) + 0) if $3; + } + elsif (/^Position:\s+(\d+),(\d+)/i){ + $pos_x = $1; + $pos_y = $2; + } + elsif (/^Scale factor:\s+([\d\.]+)/i){ + $scale = $1 + 0; + } + } + } + print 'swaymsg: ', Data::Dumper::Dumper $monitor_ids if $dbg[46]; + eval $end if $b_log; +} +# like a basic stripped down swaymsg -t get_outputs -p, less data though +# This is EXTREMELY LIKELY TO FAIL! Any tiny syntax change will break this. +sub wlrrandr_data { + eval $start if $b_log; + my ($program) = @_; + my ($file,$hz,$id,$info,$model,$pos_x,$pos_y,$res_x,$res_y,$scale,$serial); + my (@data,@temp); + if (!$fake{'wlr-randr'}){ + @data = main::grabber("$program 2>/dev/null",'','strip'); + } + else { + undef $monitor_ids; + $file = "$ENV{'HOME'}/bin/scripts/inxi/data/wayland/wlr-randr-2-monitor-1.txt"; + @data = main::reader($file,'strip'); + } + foreach (@data){ + push(@temp,'~~') if /^([A-Z]+-[ABID\d-]+)\s['"]/i; + push(@temp,$_); + } + push(@temp,'~~') if @temp; + @data = @temp; + $b_wayland_data = 1 if scalar @data > 4; + print 'wlr-randr: ', Data::Dumper::Dumper \@data if $dbg[46]; + main::log_data('dump','@data', \@data) if $b_log; + foreach (@data){ + if ($_ eq '~~' && $id){ + $monitor_ids->{$id}{'hz'} = $hz; + $monitor_ids->{$id}{'model'} = $model if $model && !$monitor_ids->{$id}{'model'}; + $monitor_ids->{$id}{'monitor'} = $id; + $monitor_ids->{$id}{'pos-x'} = $pos_x; + $monitor_ids->{$id}{'pos-y'} = $pos_y; + $monitor_ids->{$id}{'res-x'} = $res_x; + $monitor_ids->{$id}{'res-y'} = $res_y; + $monitor_ids->{$id}{'scale'} = $scale; + $monitor_ids->{$id}{'serial'} = $serial if $serial && !$monitor_ids->{$id}{'serial'}; + ($hz,$info,$model,$pos_x,$pos_y,$res_x,$res_y,$scale,$serial) = (); + $b_wayland_data = 1; + } + # Output: VGA-1 ' ' (focused) + # DVI-I-1 'Samsung Electric Company SyncMaster H9NX843762' (focused) + # unknown how 'primary' is shown, if it shows in this output + if (/^([A-Z]+-[ABID\d-]+)\s([']([^']+)['])?/i){ + $id = $1; + # if model is set, we got edid data + if ($3 && !$monitor_ids->{$id}{'model'}){ + ($model,$serial) = get_model_serial($3); + } + } + elsif (/^(\d+)x(\d+)\s+px,\s+([\d\.]+)\s+Hz \([^\)]*?current\)/i){ + $res_x = $1; + $res_y = $2; + $hz = sprintf('%.0f',$3) if $3; + } + elsif (/^Position:\s+(\d+),(\d+)/i){ + $pos_x = $1; + $pos_y = $2; + } + elsif (/^Scale:\s+([\d\.]+)/i){ + $scale = $1 + 0; + } + } + print 'wlr-randr: ', Data::Dumper::Dumper $monitor_ids if $dbg[46]; + eval $end if $b_log; +} +# return model/serial for those horrible string type values we have to process +# in swaymsg -t get_outputs -p and wlr-randr default output +sub get_model_serial { + eval $start if $b_log; + my $info = $_[0]; + my ($model,$serial); + $info = main::clean($info); + return if !$info; + my @parts = split(/\s+/, $info); + # Perl Madness, lol: the last just checks how many integers in string + if (scalar @parts > 1 && (length($parts[-1]) > 7) && + (($parts[-1] =~ tr/[0-9]//) > 4)){ + $serial = pop @parts; + $serial = main::clean_dmi($serial); # clears out 0x00000 type non data + } + # we're assuming that we'll never get a serial without make/model data too. + $model = join(' ',@parts) if @parts; + $model = main::remove_duplicates($model) if $model && scalar @parts > 1; + eval $end if $b_log; + return ($model,$serial); +} + +# DISPLAY DATA X.org ## +sub display_data_x { + eval $start if $b_log; + my ($prog_xdpyinfo,$prog_xrandr); + if ($prog_xdpyinfo = main::check_program('xdpyinfo')){ + xdpyinfo_data($prog_xdpyinfo); + } + # print Data::Dumper::Dumper $graphics{'screens'}; + if ($prog_xrandr = main::check_program('xrandr')){ + xrandr_data($prog_xrandr); + } + if (!$graphics{'screens'}){ + $graphics{'tty'} = tty_data(); + } + if (!$prog_xrandr){ + $graphics{'no-monitors'} = main::message('tool-missing-basic','xrandr'); + if (!$prog_xdpyinfo){ + if ($graphics{'protocol'} eq 'wayland'){ + $graphics{'no-screens'} = main::message('screen-wayland'); + } + else { + $graphics{'no-screens'} = main::message('tool-missing-basic','xdpyinfo/xrandr'); + } + } + } + print 'Final display x: ', Data::Dumper::Dumper $graphics{'screens'} if $dbg[17]; + main::log_data('dump','$graphics{screens}',$graphics{'screens'}) if $b_log; + eval $end if $b_log; +} +sub xdpyinfo_data { + eval $start if $b_log; + my ($program) = @_; + my ($diagonal,$diagonal_m,$dpi) = ('','',''); + my ($screen_id,$screen,@working); + my ($res_x,$res_y,$size_x,$size_x_i,$size_y,$size_y_i); + my @xdpyinfo = main::grabber("$program $display_opt 2>/dev/null","\n",'strip'); + # @xdpyinfo = map {s/^\s+//;$_} @xdpyinfo if @xdpyinfo; + # print join("\n",@xdpyinfo), "\n"; + # X vendor and version detection. + # new method added since radeon and X.org and the disappearance of + # version : ...etc. Later on, the normal textual version string + # returned, e.g. like: X.Org version: 6.8.2 + # A failover mechanism is in place: if $version empty, release number parsed instead + foreach (@xdpyinfo){ + @working = split(/:\s+/, $_); + next if (($graphics{'screens'} && $working[0] !~ /^(dimensions$|screen\s#)/) || !$working[0]); + # print "$_\n"; + if ($working[0] eq 'vendor string'){ + $working[1] =~ s/The\s|\sFoundation//g; + # some distros, like fedora, report themselves as the xorg vendor, + # so quick check here to make sure the vendor string includes Xorg in string + if ($working[1] !~ /x/i){ + $working[1] .= ' X.org'; + } + $graphics{'x-server'} = [[$working[1]]]; + } + elsif ($working[0] eq 'name of display'){ + $graphics{'display-id'} = $working[1]; + } + # this is the x protocol version + elsif ($working[0] eq 'version number'){ + $graphics{'x-protocol-version'} = $working[1]; + } + # not used, but might be good for something? + elsif ($working[0] eq 'vendor release number'){ + $graphics{'x-vendor-release'} = $working[1]; + } + # the real X.org version string + elsif ($working[0] eq 'X.Org version'){ + push(@{$graphics{'x-server'}->[0]},$working[1]); + } + elsif ($working[0] eq 'default screen number'){ + $graphics{'display-default-screen'} = $working[1]; + } + elsif ($working[0] eq 'number of screens'){ + $graphics{'display-screens'} = $working[1]; + } + elsif ($working[0] =~ /^screen #([0-9]+):/){ + $screen_id = $1; + } + elsif ($working[0] eq 'resolution'){ + $working[1] =~ s/^([0-9]+)x/$1/; + $graphics{'s-dpi'} = $working[1]; + } + # This is Screen, not monitor: dimensions: 2560x1024 pixels (677x270 millimeters) + elsif ($working[0] eq 'dimensions'){ + ($dpi,$res_x,$res_y,$size_x,$size_y) = (); + if ($working[1] =~ /([0-9]+)\s*x\s*([0-9]+)\s+pixels\s+\(([0-9]+)\s*x\s*([0-9]+)\s*millimeters\)/){ + $res_x = $1; + $res_y = $2; + $size_x = $3; + $size_y = $4; + # flip size x,y if don't roughly match res x/y ratio + if ($size_x && $size_y && $res_y){ + flip_size_x_y(\$size_x,\$size_y,\$res_x,\$res_y); + } + $size_x_i = ($size_x) ? sprintf("%.1f", ($size_x/25.4)) : 0; + $size_y_i = ($size_y) ? sprintf("%.1f", ($size_y/25.4)) : 0; + $dpi = ($res_x && $size_x) ? sprintf("%.0f", ($res_x*25.4/$size_x)) : ''; + $diagonal = ($size_x && $size_y) ? sprintf("%.1f", (sqrt($size_x**2 + $size_y**2)/25.4)) : ''; + $diagonal += 0 if $diagonal;# trick to get rid of decimal 0 + $diagonal_m = ($size_x && $size_y) ? sprintf("%.0f", (sqrt($size_x**2 + $size_y**2))) : ''; + } + $screen = { + 'screen' => $screen_id, + 'res-x' => $res_x, + 'res-y' => $res_y, + 'size-x' => $size_x, + 'size-x-i' => $size_x_i, + 'size-y' => $size_y, + 'size-y-i' => $size_y_i, + 's-dpi' => $dpi, + 'diagonal' => $diagonal, + 'diagonal-m' => $diagonal_m, + }; + push(@{$graphics{'screens'}}, $screen); + } + } + print 'Data: xdpyinfo: ', Data::Dumper::Dumper $graphics{'screens'} if $dbg[17]; + main::log_data('dump','$graphics{screens}',$graphics{'screens'}) if $b_log; + eval $end if $b_log; +} +sub xrandr_data { + eval $end if $b_log; + my ($program) = @_; + my ($diagonal,$diagonal_m,$dpi,$monitor_id,$pos_x,$pos_y,$primary); + my ($res_x,$res_x_max,$res_y,$res_y_max); + my ($screen_id,$set_as,$size_x,$size_x_i,$size_y,$size_y_i,$x_screen); + my (@ids,%monitors,@xrandr_screens,@xrandr); + if (!$fake{'xrandr'}){ + @xrandr = main::grabber("$program $display_opt 2>/dev/null",'','strip'); + } + else { + @xrandr = main::reader("$ENV{HOME}/bin/scripts/inxi/svn/branches/inxi-perl/xrandr.txt",'strip'); + } + # $graphics{'dimensions'} = (\@dimensions); + # we get a bit more info from xrandr than xdpyinfo, but xrandr fails to handle + # multiple screens from different video cards + foreach (@xrandr){ + # note: no mm as with xdpyinfo + # Screen 0: minimum 320 x 200, current 2560 x 1024, maximum 8192 x 8192 + if (/^Screen ([0-9]+):/){ + $screen_id = $1; + # handle no xdpyinfo Screen data + if ((!$graphics{'screens'} || + scalar @{$graphics{'screens'}} != (scalar @xrandr_screens + 1)) && + /:\s.*?current\s+(\d+)\s*x\s*(\d+),\smaximum\s+(\d+)\s*x\s*(\d+)/){ + $res_x = $1; + $res_y = $2; + $res_x_max = $3; + $res_y_max = $4; + $x_screen = { + 'screen' => $screen_id, + 'res-x' => $res_x, + 'res-y' => $res_y, + 'size-x' => undef, + 'size-x-i' => undef, + 'size-y' => undef, + 'size-y-i' => undef, + 's-dpi' => undef, + 'diagonal' => undef, + 'diagonal-m' => undef, + }; + push(@{$graphics{'screens'}}, $x_screen); + } + if (%monitors){ + push(@xrandr_screens,\%monitors); + %monitors = (); + } + } + # HDMI-2 connected 1920x1200+1080+0 (normal left inverted right x axis y axis) 519mm x 324mm + # DP-1 connected primary 2560x1440+1080+1200 (normal left inverted right x axis y axis) 598mm x 336mm + # HDMI-1 connected 1080x1920+0+0 left (normal left inverted right x axis y axis) 160mm x 90mm + elsif (/^([^\s]+)\s+connected\s(primary\s)?([0-9]+)\s*x\s*([0-9]+)\+([0-9]+)\+([0-9]+)(\s[^(]*\([^)]+\))?(\s([0-9]+)mm\sx\s([0-9]+)mm)?/){ + $monitor_id = $1; + $set_as = $2; + $res_x = $3; + $res_y = $4; + $pos_x = $5; + $pos_y = $6; + $size_x = $9; + $size_y = $10; + # flip size x,y if don't roughly match res x/y ratio + if ($size_x && $size_y && $res_y){ + flip_size_x_y(\$size_x,\$size_y,\$res_x,\$res_y); + } + $size_x_i = ($size_x) ? sprintf("%.1f", ($size_x/25.4)) : 0; + $size_y_i = ($size_y) ? sprintf("%.1f", ($size_y/25.4)) : 0; + $dpi = ($res_x && $size_x) ? sprintf("%.0f", $res_x * 25.4 / $size_x) : ''; + $diagonal = ($res_x && $size_x) ? sprintf("%.1f", (sqrt($size_x**2 + $size_y**2)/25.4)) : ''; + $diagonal += 0 if $diagonal; # trick to get rid of decimal 0 + $diagonal_m = ($res_x && $size_x) ? sprintf("%.0f", (sqrt($size_x**2 + $size_y**2))) : ''; + undef $primary; + push(@ids,$monitor_id); + if ($set_as){ + $primary = $monitor_id; + $set_as =~ s/\s$//; + } + $monitors{$monitor_id} = { + 'screen' => $screen_id, + 'monitor' => $monitor_id, + 'pos-x' => $pos_x, + 'pos-y' => $pos_y, + 'primary' => $primary, + 'res-x' => $res_x, + 'res-y' => $res_y, + 'size-x' => $size_x, + 'size-x-i' => $size_x_i, + 'size-y' => $size_y, + 'size-y-i' => $size_y_i, + 'dpi' => $dpi, + 'diagonal' => $diagonal, + 'diagonal-m' => $diagonal_m, + 'position' => $set_as, + }; + # print "x:$size_x y:$size_y rx:$res_x ry:$res_y dpi:$dpi\n"; + ($res_x,$res_y,$size_x,$size_x_i,$size_y,$size_y_i,$set_as) = (0,0,0,0,0,0,0,0,undef); + } + my @working = split(/\s+/,$_); + # this is the monitor current dimensions + if ($working[1] =~ /\*/){ + $working[1] =~ s/\*|\+//g; + $working[1] = sprintf("%.0f",$working[1]); + if ($monitor_id && %monitors){ + $monitors{$monitor_id}->{'hz'} = $working[1]; + } + ($diagonal,$dpi) = ('',''); + # print Data::Dumper::Dumper \@monitors; + } + } + if (%monitors){ + push(@xrandr_screens,\%monitors); + } + my $i = 0; + my $layouts; + if (!defined $graphics{'display-screens'} && $graphics{'screens'}){ + $graphics{'display-screens'} = scalar @{$graphics{'screens'}}; + } + map_monitor_ids(\@ids) if @ids; + foreach my $main (@{$graphics{'screens'}}){ + # print "h: " . Data::Dumper::Dumper $main; + # print "h: " . Data::Dumper::Dumper @xrandr_screens; + # print $main->{'screen'}, "\n"; + foreach my $x_screen (@xrandr_screens){ + # print "d: " . Data::Dumper::Dumper $x_screen; + my @keys = sort keys %$x_screen; + if ($x_screen->{$keys[0]}{'screen'} eq $main->{'screen'} && + !defined $graphics{'screens'}->[$i]{'monitors'}){ + $graphics{'screens'}->[$i]{'monitors'} = $x_screen; + } + if ($extra > 1){ + if (!$layouts){ + $layouts = []; + set_monitor_layouts($layouts); + } + advanced_monitor_data($x_screen,$layouts); + } + if (!defined $main->{'size-x'}){ + $graphics{'screens'}->[$i]{'size-missing'} = main::message('tool-missing-basic','xdpyinfo'); + } + } + $i++; + } + undef $layouts; + # print "xrand: " . Data::Dumper::Dumper \@xrandr_screens; + print 'Data: xrandr: ', Data::Dumper::Dumper $graphics{'screens'} if $dbg[17]; + main::log_data('dump','$graphics{screens}',$graphics{'screens'}) if $b_log; + eval $end if $b_log; +} +# case where no xpdyinfo display server/version data exists, or to set Wayland +# Xwayland version, or Xvesa data. +sub display_server_data { + eval $start if $b_log; + my ($program); + # load the extra X paths, it's important that these are first, because + # later Xorg versions show error if run in console or ssh if the true path + # is not used. + @paths = (qw(/usr/lib /usr/lib/xorg /usr/lib/xorg-server /usr/libexec /usr/X11R6/bin), @paths); + my (@data,$server,$version); + if (!$graphics{'x-server'} || !$graphics{'x-server'}->[0][1]){ + # IMPORTANT: both commands send version data to stderr! + if ($program = main::check_program('Xorg')){ + @data = main::grabber("$program -version 2>&1",'','strip'); + $server = 'X.org'; + } + elsif ($program = main::check_program('X')){ + @data = main::grabber("$program -version 2>&1",'','strip'); + $server = 'X.org'; + } + elsif ($program = main::check_program('Xvesa')){ + @data = main::grabber("$program -version 2>&1",'','strip'); + $server = 'Xvesa'; + $graphics{'display-driver'} = ['vesa']; + $graphics{'xvesa'} = $program; + if (!$graphics{'screens'}){ + $graphics{'no-screens'} = main::message('screen-xvesa'); + } + } + # print join('^ ', @paths), " :: $program\n"; + # print Data::Dumper::Dumper \@data; + if ($data[0]){ + if ($data[0] =~ /X.org X server (\S+)/i){ + $version = $1; + } + elsif ($data[0] =~ /XFree86 Version (\S+)/i){ + $version = $1; + $server = 'XFree86'; + } + elsif ($data[0] =~ /X Window System Version (\S+)/i){ + $version = $1; + } + elsif ($data[0] =~ /Xvesa from tinyx (\S+)/i){ + $version = $1; + $server = 'TinyX Xvesa'; + } + } + $graphics{'x-server'} = [[$server,$version]] if $server; + } + if ((!$b_display || $graphics{'protocol'} eq 'wayland') && + ($program = main::check_program('Xwayland'))){ + undef $version; + @data = main::grabber("$program -version 2>&1",'','strip'); + # Slackware Linux Project Xwayland Version 21.1.4 (12101004) + # The X.Org Foundation Xwayland Version 21.1.4 (12101004) + if (@data){ + $data[0] =~ /Xwayland Version (\S+)/; + $version = $1; + } + $graphics{'x-server'} = [] if !$graphics{'x-server'}; + push(@{$graphics{'x-server'}},['Xwayland',$version]); + } + # remove extra X paths from global @paths + @paths = grep { !/^\/usr\/lib|xorg|X11R6|libexec/ } @paths; + eval $end if $b_log; +} +# for wayland display/monitor drivers, or if no display drivers found for x +sub gpu_drivers_sys { + eval $start if $b_log; + my ($id) = @_; + my ($driver,@drivers); + # we only want list of drivers for cards with a connected monitor, and inactive + # ports are already removed by the 'all' stage. + foreach my $port (keys %{$monitor_ids}){ + if (!$monitor_ids->{$port}{'drivers'} || + ($id ne 'all' && $id ne $port) || + !$monitor_ids->{$port}{'status'} || + $monitor_ids->{$port}{'status'} ne 'connected'){ + next; + } + else { + foreach $driver (@{$monitor_ids->{$port}{'drivers'}}){ + push(@drivers,$driver); + } + } + } + if (@drivers){ + @drivers = sort(@drivers); + main::uniq(\@drivers); + } + eval $end if $b_log; + @drivers ? return \@drivers : return; +} +sub display_drivers_x { eval $start if $b_log; my ($driver,@driver_data,,%drivers); my ($alternate,$failed,$loaded,$sep,$unloaded) = ('','','','',''); @@ -14048,13 +15047,12 @@ sub x_drivers { # list is from sgfxi plus non-free drivers, plus ARM drivers my $list = join('|', qw(amdgpu apm ark armsoc atimisc ati chips cirrus cyrix fbdev fbturbo fglrx geode glide glint - i128 i740 i810-dec100 i810e i810 i815 i830 i845 i855 i865 i915 i945 - i965 iftv imstt intel ivtv mach64 mesa mga modesetting neomagic newport - nouveau nsc nvidia nv openchrome r128 radeonhd radeon - rendition s3virge s3 savage siliconmotion sisimedia sisusb sis - sunbw2 suncg14 suncg3 suncg6 sunffb sunleo suntcx - tdfx tga trident tseng unichrome v4l vboxvideo vesa vga via vmware vmwgfx - voodoo)); + i128 i740 i810-dec100 i810e i810 i815 i830 i845 i855 i865 i915 i945 i965 + iftv imstt intel ivtv mach64 mesa mga modesetting neomagic newport + nouveau nsc nvidia nv openchrome r128 radeonhd radeon rendition + s3virge s3 savage siliconmotion sisimedia sisusb sis + sunbw2 suncg14 suncg3 suncg6 sunffb sunleo suntcx tdfx tga trident tseng + unichrome v4l vboxvideo vesa vga via vmware vmwgfx voodoo)); # it's much cheaper to grab the simple pattern match then do the expensive one # in the main loop. # @xorg = grep {/Failed|Unload|Loading/} @xorg; @@ -14124,123 +15122,433 @@ sub x_drivers { $alternate .= $sep . $_; } } - @driver_data = ($loaded,$unloaded,$failed,$alternate); + if ($loaded || $unloaded || $failed || $alternate){ + @driver_data = ($loaded,$unloaded,$failed,$alternate); + } } eval $end if $b_log; - return @driver_data; + @driver_data ? return \@driver_data : return; } -# fallback if no glx x version data found -sub x_version { +sub display_protocol { eval $start if $b_log; - my ($version,@data,$program); - # load the extra X paths, it's important that these are first, because - # later Xorg versions show error if run in console or ssh if the true path - # is not used. - @paths = (qw(/usr/lib /usr/lib/xorg /usr/lib/xorg-server /usr/libexec /usr/X11R6/bin), @paths); - # IMPORTANT: both commands send version data to stderr! - if ($program = main::check_program('Xorg')){ - @data = main::grabber("$program -version 2>&1"); + $graphics{'protocol'} = ''; + if ($ENV{'XDG_SESSION_TYPE'}){ + $graphics{'protocol'} = $ENV{'XDG_SESSION_TYPE'}; } - elsif ($program = main::check_program('X')){ - @data = main::grabber("$program -version 2>&1"); + if (!$graphics{'protocol'} && $ENV{'WAYLAND_DISPLAY'}){ + $graphics{'protocol'} = $ENV{'WAYLAND_DISPLAY'}; } - elsif ($program = main::check_program('Xvesa')){ - @data = main::grabber("$program -version 2>&1"); + # can show as wayland-0 + if ($graphics{'protocol'} && $graphics{'protocol'} =~ /wayland/i){ + $graphics{'protocol'} = 'wayland'; } - # print join('^ ', @paths), " :: $program\n"; - # print Data::Dumper::Dumper \@data; - if (@data){ - foreach (@data){ - if (/^X.org X server/i){ - $version = (split(/\s+/, $_))[3]; - last; + # yes, I've seen this in 2019 distros, sigh + elsif ($graphics{'protocol'} eq 'tty'){ + $graphics{'protocol'} = ''; + } + # need to confirm that there's a point to this test, I believe no, fails out of x + # loginctl also results in the session id + if (!$graphics{'protocol'} && $b_display && $force{'display'}){ + if (my $program = main::check_program('loginctl')){ + my $id = ''; + # $id = $ENV{'XDG_SESSION_ID'}; # returns tty session in console + my @data = main::grabber("$program --no-pager --no-legend 2>/dev/null",'','strip'); + foreach (@data){ + next if /tty[v]?[0-6]$/; # freebsd: ttyv3 + $id = (split(/\s+/, $_))[0]; + last; # multiuser? too bad, we'll go for the first one } - elsif (/^X Window System Version/i){ - $version = (split(/\s+/, $_))[4]; - last; - } - elsif (/^Xvesa from/i){ - $version = (split(/\s+/, $_))[3]; - $version = "Xvesa $version" if $version; - last; + if ($id){ + my $temp = (main::grabber("$program show-session $id -p Type --no-pager --no-legend 2>/dev/null"))[0]; + $temp =~ s/Type=// if $temp; + # ssh will not show /dev/ttyx so would have passed the first test + $graphics{'protocol'} = $temp if $temp && $temp ne 'tty'; } } } - # remove extra X paths - @paths = grep { !/^\/usr\/lib|xorg|X11R6|libexec/ } @paths; + $graphics{'protocol'} = lc($graphics{'protocol'}) if $graphics{'protocol'}; eval $end if $b_log; - return $version; } -# $1 - protocol: wayland|x11 -sub display_compositor { + +## MONITOR DATA ## +sub set_monitors_sys { eval $start if $b_log; - my ($protocol) = @_; - my ($compositor) = (''); + my $pattern = '/sys/class/drm/card[0-9]/device/driver/module/drivers/*'; + my @cards_glob = main::globber($pattern); + $pattern = '/sys/class/drm/card*-*/{edid,enabled,status,modes}'; + my @ports_glob = main::globber($pattern); + # print Data::Dumper::Dumper \@cards_glob; + # print Data::Dumper::Dumper \@ports_glob; + my ($card,%cards,@data,$file,$item,$path,$port); + foreach $file (@cards_glob){ + next if ! -e $file; + if ($file =~ m|^/sys/class/drm/(card\d+)/.+?/drivers/(\S+):(\S+)$|){ + push(@{$cards{$1}},[$2,$3]); + } + } + # print Data::Dumper::Dumper \%cards; + foreach $file (sort @ports_glob){ + next if ! -r $file; + $item = $file; + $item =~ s|(/.*/(card\d+)-([^/]+))/(.+)||; + $path = $1; + $card = $2; + $port = $3; + $item = $4; + next if !$1; + $monitor_ids = {} if !$monitor_ids; + $monitor_ids->{$port}{'monitor'} = $port; + if (!$monitor_ids->{$port}{'drivers'} && $cards{$card}){ + foreach my $info (@{$cards{$card}}){ + push(@{$monitor_ids->{$port}{'drivers'}},$info->[1]); + } + } + $monitor_ids->{$port}{'path'} = readlink($path); + $monitor_ids->{$port}{'path'} =~ s|^\.\./\.\.|/sys|; + if ($item eq 'status' || $item eq 'enabled'){ + # print "$file\n"; + $monitor_ids->{$port}{$item} = main::reader($file,'strip',0); + } + # arm: U:1680x1050p-0 + elsif ($item eq 'modes'){ + @data = main::reader($file,'strip'); + next if !@data; + if (scalar @data == 1 || $data[-1] eq $data[0]){ + $monitor_ids->{$port}{'modes-min-max'} = $data[0]; + } + else { + $monitor_ids->{$port}{'modes-min'} = $data[-1]; + $monitor_ids->{$port}{'modes-max'} = $data[0]; + } + } + elsif ($item eq 'edid'){ + next if -s $file; + monitor_edid_data($file,$port); + } + } + main::log_data('dump','$ports ref',$monitor_ids) if $b_log; + print Data::Dumper::Dumper $monitor_ids if $dbg[44]; + eval $end if $b_log; +} +sub monitor_edid_data { + eval $start if $b_log; + my ($file,$port) = @_; + my (@data); + open my $fh, '<:raw', $file or return; # it failed, give up, we don't care why + my $edid_raw = do { local $/; <$fh> }; + return if !$edid_raw; + my $edid = ParseEDID::parse_edid($edid_raw,$dbg[47]); + main::log_data('dump','Parse::EDID',$edid) if $b_log; + print Data::Dumper::Dumper $edid if $dbg[44]; + return if !$edid || ref $edid ne 'HASH' || !%$edid; + $monitor_ids->{$port}{'build-date'} = $edid->{'year'}; + if ($edid->{'gamma'}){ + $monitor_ids->{$port}{'gamma'} = ($edid->{'gamma'}/100 + 0); + } + if ($edid->{'monitor_name'} || $edid->{'manufacturer_name_nice'}){ + my $model = ''; + if ($edid->{'manufacturer_name_nice'}){ + $model = $edid->{'manufacturer_name_nice'}; + } + if ($edid->{'monitor_name'}){ + $model .= ' ' if $model; + $model .= $edid->{'monitor_name'}; + } + $monitor_ids->{$port}{'model'} = main::remove_duplicates(main::clean($model)); + } + if ($edid->{'diagonal_size'}){ + $monitor_ids->{$port}{'diagonal-m'} = sprintf('%.0f',($edid->{'diagonal_size'}*25.4)) + 0; + $monitor_ids->{$port}{'diagonal'} = sprintf('%.1f',$edid->{'diagonal_size'}) + 0; + } + if ($edid->{'ratio_name'}){ + $edid->{'ratio_name'} =~ s|/|:|; # this should be changed in ParseEDID + $monitor_ids->{$port}{'ratio'} = $edid->{'ratio_name'}; + } + if ($edid->{'detailed_timings'}){ + $monitor_ids->{$port}{'res-x'} = $edid->{'detailed_timings'}[0]{'horizontal_active'}; + $monitor_ids->{$port}{'res-y'} = $edid->{'detailed_timings'}[0]{'vertical_active'}; + if ($edid->{'detailed_timings'}[0]{'horizontal_image_size'}){ + $monitor_ids->{$port}{'size-x'} = $edid->{'detailed_timings'}[0]{'horizontal_image_size'}; + $monitor_ids->{$port}{'size-x-i'} = sprintf('%.1f',($edid->{'detailed_timings'}[0]{'horizontal_image_size'}/25.4)) + 0; + } + if ($edid->{'detailed_timings'}[0]{'vertical_image_size'}){ + $monitor_ids->{$port}{'size-y'} = $edid->{'detailed_timings'}[0]{'vertical_image_size'}; + $monitor_ids->{$port}{'size-y-i'} = sprintf('%.1f',($edid->{'detailed_timings'}[0]{'vertical_image_size'}/25.4)) + 0; + } + if ($edid->{'detailed_timings'}[0]{'horizontal_dpi'}){ + $monitor_ids->{$port}{'dpi'} = sprintf('%.0f',$edid->{'detailed_timings'}[0]{'horizontal_dpi'}) + 0; + } + } + if ($edid->{'serial_number'} || $edid->{'serial_number2'}){ + # this looks much more like a real serial than the default: serial_number + if ($edid->{'serial_number2'} && @{$edid->{'serial_number2'}}){ + $monitor_ids->{$port}{'serial'} = main::clean_dmi($edid->{'serial_number2'}[0]); + } + elsif ($edid->{'serial_number'}){ + $monitor_ids->{$port}{'serial'} = main::clean_dmi($edid->{'serial_number'}); + } + } + eval $end if $b_log; +} +sub advanced_monitor_data { + eval $start if $b_log; + my ($monitors,$layouts) = @_; + my (@horiz,@vert); + my $position = ''; + # then see if we can locate a default position primary monitor + foreach my $key (keys %$monitors){ + next if !defined $monitors->{$key}{'pos-x'}; + # this is the only scenario we can guess at if no primary detected + if (!$monitors->{$key}{'primary'} && + $monitors->{$key}{'pos-x'} == 0 && $monitors->{$key}{'pos-y'} == 0){ + $monitors->{$key}{'position'} = 'primary'; + $monitors->{$key}{'primary'} = $monitors->{$key}{'monitor'}; + } + if (!grep {$monitors->{$key}{'pos-x'} == $_} @horiz){ + push(@horiz,$monitors->{$key}{'pos-x'}); + } + if (!grep {$monitors->{$key}{'pos-y'} == $_} @vert){ + push(@vert,$monitors->{$key}{'pos-y'}); + } + } + @horiz = sort(@horiz); + @vert = sort(@vert); + my ($h,$v) = (scalar(@horiz),scalar(@vert)); + # print Data::Dumper::Dumper \@horiz; + # print Data::Dumper::Dumper \@vert; + # print Data::Dumper::Dumper $layouts; + # print Data::Dumper::Dumper $monitor_map; + foreach my $key (keys %$monitors){ + if (@horiz && @vert){ + $monitors->{$key}{'position'} ||= ''; + $position = ''; + $position = get_monitor_position($monitors->{$key},\@horiz,\@vert); + $position = $layouts->[$v][$h]{$position} if $layouts->[$v][$h]{$position}; + $monitors->{$key}{'position'} .= ',' if $monitors->{$key}{'position'}; + $monitors->{$key}{'position'} .= $position; + } + my $mon_mapped = ($monitor_map) ? $monitor_map->{$monitors->{$key}{'monitor'}} : undef; + # these are already set for monitor_ids, only need this for Xorg data. + if ($mon_mapped && $monitor_ids->{$mon_mapped}){ + # note: xorg drivers can be different than gpu drivers + $monitors->{$key}{'drivers'} = gpu_drivers_sys($mon_mapped); + $monitors->{$key}{'build-date'} = $monitor_ids->{$mon_mapped}{'build-date'}; + $monitors->{$key}{'diagonal'} = $monitor_ids->{$mon_mapped}{'diagonal'}; + $monitors->{$key}{'diagonal-m'} = $monitor_ids->{$mon_mapped}{'diagonal-m'}; + $monitors->{$key}{'gamma'} = $monitor_ids->{$mon_mapped}{'gamma'}; + $monitors->{$key}{'modes-min-max'} = $monitor_ids->{$mon_mapped}{'modes-min-max'}; + $monitors->{$key}{'modes-max'} = $monitor_ids->{$mon_mapped}{'modes-max'}; + $monitors->{$key}{'modes-min'} = $monitor_ids->{$mon_mapped}{'modes-min'}; + $monitors->{$key}{'model'} = $monitor_ids->{$mon_mapped}{'model'}; + $monitors->{$key}{'ratio'} = $monitor_ids->{$mon_mapped}{'ratio'}; + $monitors->{$key}{'serial'} = $monitor_ids->{$mon_mapped}{'serial'}; + if ($mon_mapped ne $monitors->{$key}{'monitor'}){ + $monitors->{$key}{'monitor-mapped'} = $mon_mapped; + } + } + } + # not printing out primary if Screen has only 1 Monitor + if (scalar keys %$monitors == 1){ + my @keys = keys %$monitors; + $monitors->{$keys[0]}{'position'} = undef; + } + print Data::Dumper::Dumper $monitors if $dbg[45]; + eval $end if $b_log; +} +# clear out all disabled or not connected monitor ports +sub set_active_monitors { + eval $start if $b_log; + foreach my $key (keys %$monitor_ids){ + if (!$monitor_ids->{$key}{'enabled'} || + $monitor_ids->{$key}{'enabled'} ne 'enabled' || + !$monitor_ids->{$key}{'status'} || + $monitor_ids->{$key}{'status'} ne 'connected'){ + delete $monitor_ids->{$key}; + } + } + # print 'active monitors: ', Data::Dumper::Dumper $monitor_ids; + eval $end if $b_log; +} +sub get_monitor_position { + eval $start if $b_log; + my ($monitor,$horiz,$vert) = @_; + my ($i,$position) = (1,''); + foreach (@$vert){ + if ($_ == $monitor->{'pos-y'}){ + $position = $i . '-'; + last; + } + $i++; + } + $i = 1; + foreach (@$horiz){ + if ($_ == $monitor->{'pos-x'}){ + $position .= $i; + last; + } + $i++; + } + main::log_data('data','pos-raw: ' . $position) if $b_log; + eval $end if $b_log; + return $position; +} +sub set_monitor_layouts { + my ($layouts) = @_; + $layouts->[1][2] = {'1-1' => 'left','1-2' => 'right'}; + $layouts->[1][3] = {'1-1' => 'left','1-2' => 'center','1-3' => 'right'}; + $layouts->[1][4] = {'1-1' => 'left','1-2' => 'center-l','1-3' => 'center-r', + '1-4' => 'right'}; + $layouts->[2][1] = {'1-1' => 'top','2-1' => 'bottom'}; + $layouts->[2][2] = {'1-1' => 'top-left','1-2' => 'top-right', + '2-1' => 'bottom-l','2-2' => 'bottom-r'}; + $layouts->[2][3] = {'1-1' => 'top-left','1-2' => 'top-center','1-3' => 'top-right', + '2-1' => 'bottom-l','2-2' => 'bottom-c','2-3' => 'bottom-r'}; + $layouts->[3][1] = {'1-1' => 'top','2-1' => 'middle','3-1' => 'bottom'}; + $layouts->[3][2] = {'1-1' => 'top-left','1-2' => 'top-right', + '2-1' => 'middle-l','2-2' => 'middle-r', + '3-1' => 'bottom-l','3-2' => 'bottom-r'}; + $layouts->[3][3] = {'1-1' => 'top-left','1-2' => 'top-center',,'1-3' => 'top-right', + '2-1' => 'middle-l','2-2' => 'middle-c','2-3' => 'middle-r', + '3-1' => 'bottom-l','3-2' => 'bottom-c','3-3' => 'bottom-r'}; +} +# this is required to resolve the insane situation where some xorg drivers change +# the kernel ID for the port to something slightly different, amdgpu in particular. +sub map_monitor_ids { + eval $start if $b_log; + my ($display_ids) = @_; + return if !$monitor_ids; + @$display_ids = sort { lc($a) cmp lc($b) } @$display_ids; + my @sys_ids; + foreach my $key (keys %$monitor_ids){ + if ($monitor_ids->{$key}{'status'} eq 'connected' && + $monitor_ids->{$key}{'enabled'} eq 'enabled'){ + push(@sys_ids,$key); + } + } + @sys_ids = sort { lc($a) cmp lc($b) } @sys_ids; + # @sys_ids = ('DP-1-1','DVI-I-1','VGA-1'); + main::log_data('dump','@sys_ids',\@sys_ids) if $b_log; + main::log_data('dump','$xrandr_ids ref',$display_ids) if $b_log; + print 'sys: ', Data::Dumper::Dumper \@sys_ids if $dbg[45]; + print 'display: ', Data::Dumper::Dumper $display_ids if $dbg[45]; + return if scalar @sys_ids != scalar @$display_ids; + $monitor_map = {}; + # known patterns: s: DP-1, d: DisplayPort-0; s: HDMI-A-2, d: HDMI-A-1 + # s: HDMI-A-2, d: HDMI-2; s: DVI-1 d: DVI1; s: HDMI-1, d: HDMI1 + # s: DVI-I-1, d: DVI0; s: VGA-1, d: VGA1; s: DP-1-1; d: DP-1-1 + my ($d_1,$d_2,$d_3,$d_4,$s_1,$s_2,$s_3,$s_4); + for (my $i=0; $i < scalar @$display_ids; $i++){ + print "s: $sys_ids[$i] x: $display_ids->[$i]\n" if $dbg[45]; + if ($display_ids->[$i] =~ /^([A-Z]+)(-[AB]|-[ADI]|-[ADI]-\d+?|-\d+?)?(-)?(\d+)$/i){ + $d_1 = $1; + $d_2 = ($2) ? $2 : ''; + $d_3 = ($3) ? $3 : ''; + $d_4 = $4; + $d_1 =~ s/^DisplayPort/DP/i; # amdgpu... + print " d1: $d_1 d2: $d_2 d3: $d_3 d4: $d_4\n" if $dbg[45]; + if ($sys_ids[$i] =~ /^([A-Z]+?)(-[AB]|-[ADI]|-[ADI]-\d+?|-\d+?)?(-)?(\d+)$/i){ + $s_1 = $1; + $s_2 = ($2) ? $2 : ''; + $s_3 = ($3) ? $3 : ''; + $s_4 = $4; + $d_1 = $s_1 if uc($d_1) eq 'XWAYLAND'; + print " d1: $d_1 s1: $s_1 s2: $s_2 s3: $s_3 s4: $s_4\n" if $dbg[45]; + if ($d_1 eq $s_1 && ($d_4 == $s_4 || $d_4 == ($s_4 - 1))){ + $monitor_map->{$display_ids->[$i]} = $sys_ids[$i]; + } + } + } + if (!$monitor_map->{$display_ids->[$i]}){ + $monitor_map->{$display_ids->[$i]} = main::message('monitor-id'); + } + } + main::log_data('dump','$monitor_map ref',$monitor_map) if $b_log; + print Data::Dumper::Dumper $monitor_map if $dbg[45]; + eval $end if $b_log; +} +# handle case of monitor on left or right edge, vertical that is. +# mm dimensiions are based on the default position of monitor as sold. +# very old systems may not have non 0 value for size x or y +# size, res x,y by reference +sub flip_size_x_y { + eval $start if $b_log; + my ($size_x,$size_y,$res_x,$res_y) = @_; + if ((($$res_x/$$res_y > 1 && $$size_x/$$size_y < 1) || + ($$res_x/$$res_y < 1 && $$size_x/$$size_y > 1))){ + ($$size_x,$$size_y) = ($$size_y,$$size_x); + } + eval $end if $b_log; +} +## COMPOSITOR DATA ## +sub set_compositor_data { + eval $start if $b_log; + if (my $compositors = get_compositors()){ + my @data; + foreach my $compositor (@$compositors){ + # gnome-shell is incredibly slow to return version + if (($extra > 1 || $graphics{'protocol'} eq 'wayland') && + (!$show{'system'} || $compositor ne 'gnome-shell')){ + $graphics{'compositors'} = [] if !$graphics{'compositors'}; + push(@{$graphics{'compositors'}},[main::program_data($compositor,$compositor)]); + } + else { + $graphics{'compositors'} = [] if !$graphics{'compositors'}; + push(@{$graphics{'compositors'}},[(main::program_values($compositor))[3]]); + } + } + } + eval $end if $b_log; +} +sub get_compositors { + eval $start if $b_log; + my @found; main::set_ps_gui() if !$loaded{'ps-gui'}; if (@ps_gui){ - # 1 check program; 2 search; 3 unused version; 4 print - my @compositors = ( - ['asc','asc','','asc'], - ['budgie-wm','budgie-wm','','budgie-wm'], - # owned by: compiz-core in debian - ['compiz','compiz','','compiz'], - ['compton','compton','','compton'], - # as of version 20 is wayland compositor - ['enlightenment','enlightenment','','enlightenment'], - ['gnome-shell','gnome-shell','','gnome-shell'], - ['kwin_wayland','kwin_wayland','','kwin_wayland'], - ['kwin_x11','kwin_x11','','kwin_x11'], - # ['kwin','kwin','','kwin'], - ['marco','marco','','marco'], - ['muffin','muffin','','muffin'], - ['mutter','mutter','','mutter'], - ['weston','weston','','weston'], - # these are more obscure, so check for them last - ['3dwm','3dwm','','3dwm'], - ['dcompmgr','dcompmgr','','dcompmgr'], - ['dwc','dwc','','dwc'], - ['fireplace','fireplace','','fireplace'], - ['grefson','grefson','','grefson'], - ['kmscon','kmscon','','kmscon'], - ['liri','liri','','liri'], - ['metisse','metisse','','metisse'], - ['mir','mir','','mir'], - ['moblin','moblin','','moblin'], - ['motorcar','motorcar','','motorcar'], - ['monsterwm','monsterwm','','monsterwm'], - ['orbital','orbital','','orbital'], - ['papyros','papyros','','papyros'], - ['perceptia','perceptia','','perceptia'], - ['picom','picom','','picom'], - ['rustland','rustland','','rustland'], - ['sommelier','sommelier','','sommelier'], - ['sway','sway','','sway'], - ['swc','swc','','swc'], - ['ukwm','ukwm','','ukwm'], - ['unagi','unagi','','unagi'], - ['unity-system-compositor','unity-system-compositor','','unity-system-compositor'], - ['way-cooler','way-cooler','','way-cooler'], - ['wavy','wavy','','wavy'], - ['wayfire','wayfire','','wayfire'], - ['wayhouse','wayhouse','','wayhouse'], - ['westford','westford','','westford'], - ['xcompmgr','xcompmgr','','xcompmgr'], - ['xfwm4','xfwm4','','xfwm4'], - ['xfwm5','xfwm5','','xfwm5'], - ['xfwm','xfwm','','xfwm'], - ); - foreach my $item (@compositors){ - # no need to use check program with short list of ps_gui - # if (main::check_program($item[0]) && (grep {/^$item[1]$/} @ps_gui)){ - if (grep {/^$item->[1]$/} @ps_gui){ - $compositor = $item->[3]; - last; + # ORDER MATTES! + # notes: compiz: debian package compiz-core; + # enlightenment: as of version 20 wayland compositor + my @compositors = qw(budgie-wm compiz compton enlightenment gnome-shell + kwin_wayland kwin_x11 kwinft marco muffin mutter); + # these are more obscure, so check for them after primary common ones + push (@compositors,qw(3dwm dcompmgr gala kmscon metisse mir moblin + monsterwm picom ukwm unagi unity-system-compositor xcompmgr xfwm4 + xfwm5 xfwm)); + my $matches = join('|',@compositors) . $wl_compositors; + foreach my $psg (@ps_gui){ + if ($psg =~ /^($matches)$/){ + push(@found,$1); } } } - main::log_data('data',"compositor: $compositor") if $b_log; + main::log_data('dump','compositors:', \@found) if $b_log; eval $end if $b_log; - return $compositor; + (@found) ? return \@found : return; +} + +## UTILITIES ## +sub tty_data(){ + eval $start if $b_log; + my ($tty); + if ($size{'term-cols'}){ + $tty = "$size{'term-cols'}x$size{'term-lines'}"; + } + # this is broken + elsif ($b_irc && $client{'console-irc'}){ + ShellData::console_irc_tty() if !$loaded{'con-irc-tty'}; + my $tty_working = $client{'con-irc-tty'}; + if ($tty_working ne '' && (my $program = main::check_program('stty'))){ + my $tty_arg = ($bsd_type) ? '-f' : '-F'; + # handle vtnr integers, and tty ID with letters etc. + $tty_working = "tty$tty_working" if -e "/dev/tty$tty_working"; + $tty = (main::grabber("$program $tty_arg /dev/$tty_working size 2>/dev/null"))[0]; + if ($tty){ + my @temp = split(/\s+/, $tty); + $tty = "$temp[1]x$temp[0]"; + } + } + } + eval $end if $b_log; + return $tty; } } @@ -14281,7 +15589,7 @@ sub get { },); } elsif (@lsblk && !$use{'logical-lvm'} && ($alerts{'lvs'}->{'action'} eq 'permissions' || - $alerts{'lvs'}->{'action'} eq 'missing')){ + $alerts{'lvs'}->{'action'} eq 'missing')){ my $key = 'Message'; push(@rows, { main::key($num++,0,1,$key) => main::message('logical-data',''), @@ -14445,9 +15753,9 @@ sub general_data { %parent = LsblkData::get($row->{'parent'}); next if !$parent{'fs'}; if ($row->{'type'} && (($row->{'type'} eq 'crypt' || - $row->{'type'} eq 'mpath' || $row->{'type'} eq 'multipath') || - ($row->{'type'} eq 'dm' && $row->{'name'} =~ /veracrypt/i) || - ($parent{'fs'} eq 'bcache'))){ + $row->{'type'} eq 'mpath' || $row->{'type'} eq 'multipath') || + ($row->{'type'} eq 'dm' && $row->{'name'} =~ /veracrypt/i) || + ($parent{'fs'} eq 'bcache'))){ my (@full_components,$mapped,$type); $mapped = $mapper{$row->{'name'}} if %mapper; next if grep(/^$row->{'name'}$/, @found); @@ -14656,6 +15964,18 @@ sub get { } } } + elsif ($fake{'elbrus'} || $cpu_arch eq 'elbrus'){ + if ($fake{'elbrus'} || (my $program = main::check_program('fruid_print'))){ + %data = machine_data_fruid($program); + } + if (%data){ + @rows = machine_output(\%data); + } + elsif (!$key1){ + $key1 = 'Message'; + $val1 = main::message('machine-data-fruid'); + } + } elsif (!$bsd_type){ # this uses /proc/cpuinfo so only GNU/Linux if (%risc){ @@ -14696,8 +16016,9 @@ sub machine_output { # print "$key: $data->{$key}\n"; # } if (!$data->{'sys_vendor'} || - ($data->{'board_vendor'} && $data->{'sys_vendor'} eq $data->{'board_vendor'} && - !$data->{'product_name'} && !$data->{'product_version'} && !$data->{'product_serial'})){ + ($data->{'board_vendor'} && $data->{'sys_vendor'} eq $data->{'board_vendor'} && + !$data->{'product_name'} && !$data->{'product_version'} && + !$data->{'product_serial'})){ $b_skip_system = 1; } # The goal here is to not show laptop/mobile devices @@ -14708,11 +16029,11 @@ sub machine_output { # in case where the vendor is the same and the version is the same and not null, # otherwise the version information is going to be different in all cases I think if (($data->{'sys_vendor'} && $data->{'board_vendor'} && - $data->{'sys_vendor'} eq $data->{'board_vendor'}) && - (($data->{'product_version'} && $data->{'board_version'} && - $data->{'product_version'} eq $data->{'board_version'}) || - (!$data->{'product_version'} && $data->{'product_name'} && $data->{'board_name'} && - $data->{'product_name'} eq $data->{'board_name'}))){ + $data->{'sys_vendor'} eq $data->{'board_vendor'}) && + (($data->{'product_version'} && $data->{'board_version'} && + $data->{'product_version'} eq $data->{'board_version'}) || + (!$data->{'product_version'} && $data->{'product_name'} && $data->{'board_name'} && + $data->{'product_name'} eq $data->{'board_name'}))){ $b_skip_system = 1; } } @@ -14797,6 +16118,9 @@ sub machine_output { if ($extra > 2 && $data->{'board_uuid'}){ $rows[$j]->{main::key($num++,0,3,'uuid')} = $data->{'board_uuid'}; } + if ($extra > 1 && $data->{'board_mfg_date'}){ + $rows[$j]->{main::key($num++,0,3,'mfg-date')} = $data->{'board_mfg_date'}; + } $rows[$j]->{main::key($num++,1,1,$firmware)} = $bios_vendor; $rows[$j]->{main::key($num++,0,2,'v')} = $bios_version; if ($bios_rev){ @@ -14846,7 +16170,48 @@ sub machine_soc_output { eval $end if $b_log; return @rows; } - +sub machine_data_fruid { + eval $start if $b_log; + my ($program) = @_; + my ($b_start,%data,@fruid); + if (!$fake{'elbrus'}){ + @fruid = main::grabber("$program 2>/dev/null",'','strip'); + } + else { + # my $file; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/fruid/fruid-e904-1_full.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/fruid/fruid-e804-1_full.txt"; + # @fruid = main::reader($file,'strip'); + } + # print Data::Dumper::Dumper \@fruid; + foreach (@fruid){ + $b_start = 1 if /^Board info/; + next if !$b_start; + my @split = split(/\s*:\s+/,$_,2); + if ($split[0] eq 'Mfg. Date/Time'){ + $data{'board_mfg_date'} = $split[1]; + $data{'board_mfg_date'} =~ s/^(\d+:\d+)\s//; + } + elsif ($split[0] eq 'Board manufacturer'){ + $data{'board_vendor'} = $split[1]; + } + elsif ($split[0] eq 'Board part number'){ + $data{'board_part_nu'} = $split[1]; + } + elsif ($split[0] eq 'Board product name'){ + $data{'board_name'} = $split[1]; + } + elsif ($split[0] eq 'Board serial number'){ + $data{'board_serial'} = $split[1]; + } + elsif ($split[0] eq 'Board product version'){ + $data{'board_version'} = $split[1]; + } + } + print Data::Dumper::Dumper \%data if $dbg[28]; + main::log_data('dump','%data',\%data) if $b_log; + return %data; +} sub machine_data_sys { eval $start if $b_log; my (%data,$path,$vm); @@ -15454,6 +16819,9 @@ sub device_output { $rows[$j]->{main::key($num++,0,3,'modules')} = $row->[10] if $row->[10]; } $row->[8] ||= 'N/A'; + if ($extra > 1 && $bus_id ne 'N/A'){ + main::get_pcie_data($bus_id,$j,\@rows,\$num); + } # as far as I know, wifi has no port, but in case it does in future, use it $rows[$j]->{main::key($num++,0,2,'port')} = $row->[8] if (!$b_wifi || ($b_wifi && $row->[8] ne 'N/A')); $rows[$j]->{main::key($num++,0,2,'bus-ID')} = $bus_id; @@ -16749,7 +18117,7 @@ sub swap_data { } sub swap_advanced_data { eval $start if $b_log; - my ($swappiness,$cache_pressure) = (undef,undef); + my ($swappiness,$cache_pressure) = (); if (-r '/proc/sys/vm/swappiness'){ $swappiness = main::reader('/proc/sys/vm/swappiness','',0); if (defined $swappiness){ @@ -18394,7 +19762,7 @@ sub check_zfs_status { ## RamItem { package RamItem; -my (@vendors,%vendor_ids); +my ($vendors,$vendor_ids); sub get { my (@data,@rows,$key1,@ram,$val1); my $num = 0; @@ -18436,6 +19804,7 @@ sub get { }); } push(@rows,@data); + ($vendors,$vendor_ids) = (); eval $end if $b_log; return @rows; } @@ -18511,7 +19880,7 @@ sub ram_output { $rows[$j]->{main::key($num++,0,3,'info')} = $mod->{'type'}; } if ($mod->{'speed'} && $mod->{'configured-clock-speed'} && - $mod->{'speed'} ne $mod->{'configured-clock-speed'}){ + $mod->{'speed'} ne $mod->{'configured-clock-speed'}){ $rows[$j]->{main::key($num++,1,3,'speed')} = ''; $rows[$j]->{main::key($num++,0,4,'spec')} = $mod->{'speed'}; $rows[$j]->{main::key($num++,0,4,'note')} = $mod->{'speed-note'} if $mod->{'speed-note'}; @@ -18801,9 +20170,9 @@ sub dmidecode_data { $part_number = $result[1] if $result[1]; } if ($vendor_id && !$manufacturer){ - set_vendor_ids() if !%vendor_ids; - if ($vendor_ids{$vendor_id}){ - $manufacturer = $vendor_ids{$vendor_id}; + set_vendor_ids() if !$vendor_ids; + if ($vendor_ids->{$vendor_id}){ + $manufacturer = $vendor_ids->{$vendor_id}; } } $ram[$handle]->{'derived-module-size'} = $derived_module_size; @@ -18959,7 +20328,7 @@ sub process_data { } # 2: now check to see if actually found module sizes are > than listed max module, replace if > if ($item->{'max-module-size'} && $item->{'derived-module-size'} && - $item->{'derived-module-size'} > $item->{'max-module-size'}){ + $item->{'derived-module-size'} > $item->{'max-module-size'}){ $item->{'max-module-size'} = $item->{'derived-module-size'}; $est_mod = $est; } @@ -18974,13 +20343,13 @@ sub process_data { if ($item->{'used-capacity'} && $item->{'max-capacity-16'}){ if ($item->{'used-capacity'} > $max_cap){ if ($item->{'max-module-size'} && - $item->{'used-capacity'} < ($item->{'slots-16'} * $item->{'max-module-size'})){ + $item->{'used-capacity'} < ($item->{'slots-16'} * $item->{'max-module-size'})){ $max_cap = $item->{'slots-16'} * $item->{'max-module-size'}; $est_cap = $est; print "A\n" if $b_debug; } elsif ($item->{'derived-module-size'} && - $item->{'used-capacity'} < ($item->{'slots-16'} * $item->{'derived-module-size'})){ + $item->{'used-capacity'} < ($item->{'slots-16'} * $item->{'derived-module-size'})){ $max_cap = $item->{'slots-16'} * $item->{'derived-module-size'}; $est_cap = $est; print "B\n" if $b_debug; @@ -18997,7 +20366,7 @@ sub process_data { if (!$est_cap){ # do not do this for only single modules found, max mod size can be equal to the array size if ($item->{'slots-16'} > 1 && $item->{'device-count-found'} > 1 && - $max_cap < ($item->{'derived-module-size'} * $item->{'slots-16'})){ + $max_cap < ($item->{'derived-module-size'} * $item->{'slots-16'})){ $max_cap = $item->{'derived-module-size'} * $item->{'slots-16'}; $est_cap = $est; print "D\n" if $b_debug; @@ -19010,9 +20379,9 @@ sub process_data { ## handle cases where we have type 5 data: mms x device count equals type 5 max cap # however do not use it if cap / devices equals the derived module size elsif ($item->{'max-module-size'} > 0 && - ($item->{'max-module-size'} * $item->{'slots-16'}) == $item->{'max-capacity-5'} && - $item->{'max-capacity-5'} != $item->{'max-capacity-16'} && - $item->{'derived-module-size'} != ($item->{'max-capacity-16'}/$item->{'slots-16'})){ + ($item->{'max-module-size'} * $item->{'slots-16'}) == $item->{'max-capacity-5'} && + $item->{'max-capacity-5'} != $item->{'max-capacity-16'} && + $item->{'derived-module-size'} != ($item->{'max-capacity-16'}/$item->{'slots-16'})){ $max_cap = $item->{'max-capacity-5'}; $est_cap = $est; print "F\n" if $b_debug; @@ -19192,7 +20561,7 @@ sub speed_mapper { return ($speeds{$type}) ? $speeds{$type} . ' MT/s' : $type; } sub set_vendors { - @vendors = ( + $vendors = [ # A-Data xpg: AX4U; AX\d{4} for axiom ['^(A[DX]\dU|AVD|A[\s-]?Data)','A[\s-]?Data','A-Data',''], ['^(A[\s-]?Tech)','A[\s-]?Tech','A-Tech',''], # don't know part nu @@ -19216,6 +20585,7 @@ sub set_vendors { # ['^(HYS]|Qimonda)','Qimonda','Qimonda',''], ['^(HY|Infineon)','Infineon','Infineon',''],#HY[A-Z]\d ['^(KSM|KVR|Kingston)','Kingston','Kingston',''], + ['^(LuminouTek)','LuminouTek','LuminouTek',''], ['^(MT|Micron)','Micron','Micron',''], # seen: 992069 991434 997110S ['^(M[BLERS][A-Z][1-7]|99[0-9]{3}|Mushkin)','Mushkin','Mushkin',''], @@ -19235,11 +20605,11 @@ sub set_vendors { ['^(TR\d|JM\d|Transcend)','Transcend','Transcend',''], ['^(VK\d|Vaseky)','Vaseky','Vaseky',''], ['^(Yangtze|Zhitai)','Yangtze(\s*Memory)?','Yangtze Memory',''], - ); + ]; } # note: many of these are pci ids, not confirmed valid for ram sub set_vendor_ids { - %vendor_ids = { + $vendor_ids = { '01f4' => 'Transcend',# confirmed '02fe' => 'Elpida',# confirmed '0314' => 'Mushkin',# confirmed @@ -19274,9 +20644,9 @@ sub set_vendor_ids { sub ram_vendor { eval $end if $b_log; my ($id) = $_[0]; - set_vendors() if !@vendors; + set_vendors() if !$vendors; my ($vendor,@data); - foreach my $row (@vendors){ + foreach my $row (@$vendors){ if ($id =~ /$row->[0]/i){ $vendor = $row->[2]; # Usually we want to assign N/A at output phase, maybe do this logic there? @@ -20145,8 +21515,8 @@ sub get { # we're allowing 1 or 2 ipmi tools, first the gnu one, then the # almost certain to be present in BSDs if ($fake{'ipmi'} || (main::globber('/dev/ipmi**') && - (($program = main::check_program('ipmi-sensors')) || - ($program = main::check_program('ipmitool'))))){ + (($program = main::check_program('ipmi-sensors')) || + ($program = main::check_program('ipmitool'))))){ if ($fake{'ipmi'} || $b_root){ %sensors = ipmi_data($program); @data = sensors_output('ipmi',\%sensors); @@ -20315,7 +21685,7 @@ sub sensors_output { my $gpu_unit = (defined $gpu[0]->{'temp-unit'}) ? " $gpu[0]->{'temp-unit'}" : ' C'; foreach my $info (@gpu){ # speed unit is either '' or % - my $gpu_fan = (defined $info->{'fan-speed'}) ? $info->{'fan-speed'} . $info->{'speed-unit'}: undef ; + my $gpu_fan = (defined $info->{'fan-speed'}) ? $info->{'fan-speed'} . $info->{'speed-unit'}: undef; my $gpu_type = $info->{'type'}; my $gpu_temp = (defined $info->{'temp'}) ? $info->{'temp'} . $gpu_unit: 'N/A'; $rows[$j]->{main::key($num++,1,2,'device')} = $gpu_type; @@ -20338,7 +21708,8 @@ sub sensors_output { } } if ($extra > 0 && ($source eq 'ipmi' || - ($sensors->{'volts-12'} || $sensors->{'volts-5'} || $sensors->{'volts-3.3'} || $sensors->{'volts-vbat'}))){ + ($sensors->{'volts-12'} || $sensors->{'volts-5'} || $sensors->{'volts-3.3'} || + $sensors->{'volts-vbat'}))){ $j = scalar @rows; $sensors->{'volts-12'} ||= 'N/A'; $sensors->{'volts-5'} ||= 'N/A'; @@ -20377,6 +21748,7 @@ sub ipmi_data { # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensors-crazy-epyc-1.txt";$program='ipmitool'; # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensors-RK016013.txt";$program='ipmitool'; # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensors-freebsd-offsite-backup.txt"; + # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmitool-sensor-tyan-1.txt"; # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmi-sensors-crazy-epyc-1.txt"; # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmi-sensors-lathander.txt"; # $file = "$ENV{'HOME'}/bin/scripts/inxi/data/ipmitool/ipmi-sensors-zwerg.txt"; @@ -20413,7 +21785,7 @@ sub ipmi_data { $working_unit =~ s/degrees\s// if $b_ipmitool; $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit; } - elsif ($row[$i_key] =~ /^(Ambient)([\s_]Temp)?$/i){ + elsif ($row[$i_key] =~ /^(System\s)?(Ambient)([\s_]Temp)?$/i){ $sensors{'ambient-temp'} = int($row[$i_value]); $working_unit = $row[$i_unit]; $working_unit =~ s/degrees\s// if $b_ipmitool; @@ -20422,14 +21794,14 @@ sub ipmi_data { # Platform Control Hub (PCH), it is the X370 chip on the Crosshair VI Hero. # VRM: voltage regulator module # NOTE: CPU0_TEMP CPU1_TEMP is possible, unfortunately; CPU Temp Interf - elsif (!$sensors{'cpu-temp'} && $row[$i_key] =~ /^CPU([01])?([\s_]Temp)?$/i){ + elsif (!$sensors{'cpu-temp'} && $row[$i_key] =~ /^CPU([01])?([\s_](below[\s_]Tmax|Temp))?$/i){ $b_cpu_0 = 1 if defined $1 && $1 == 0; $sensors{'cpu-temp'} = int($row[$i_value]); $working_unit = $row[$i_unit]; $working_unit =~ s/degrees\s// if $b_ipmitool; $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit; } - elsif ($row[$i_key] =~ /^CPU([1-4])([\s_]Temp)?$/i){ + elsif ($row[$i_key] =~ /^CPU([1-4])([\s_](below[\s_]Tmax|Temp))?$/i){ $temp_working = $1; $temp_working++ if $b_cpu_0; $sensors{"cpu${temp_working}-temp"} = int($row[$i_value]); @@ -20466,19 +21838,23 @@ sub ipmi_data { } $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit; } - elsif (!$sensors{'sodimm-temp'} && $row[$i_key] =~ /^(DIMM[-_]([A-Z][0-9][-_])?[A-Z]?[0-9][A-Z]?)$/i){ + elsif (!$sensors{'sodimm-temp'} && ($row[$i_key] =~ /^(DIMM[-_]([A-Z][0-9][-_])?[A-Z]?[0-9][A-Z]?)$/i || + $row[$i_key] =~ /^DIMM[0-9] Area.*/)){ $sensors{'sodimm-temp'} = int($row[$i_value]); $working_unit = $row[$i_unit]; $working_unit =~ s/degrees\s// if $b_ipmitool; $sensors{'temp-unit'} = set_temp_unit($sensors{'temp-unit'},$working_unit) if $working_unit; } # note: can be cpu fan:, cpu fan speed:, etc. - elsif ($row[$i_key] =~ /^(CPU|Processor)[\s_]Fan/i){ + elsif ($row[$i_key] =~ /^(CPU|Processor)[\s_]Fan/i || + $row[$i_key] =~ /^SYS\.[0-9][\s_]?\(CPU\s?0\)$/i){ $sensors{'fan-main'}->[1] = int($row[$i_value]); } # note that the counters are dynamically set for fan numbers here # otherwise you could overwrite eg aux fan2 with case fan2 in theory # note: cpu/mobo/ps are 1/2/3 + # SYS.3(Front 2) + # $row[$i_key] =~ /^(SYS[\.])([0-9])\s?\((Front|Rear).+\)$/i elsif ($row[$i_key] =~ /^(SYS[\s_])?FAN[\s_]?([0-9A-F]+)/i){ $sys_fan_nu = hex($2); $fan_working = int($row[$i_value]); @@ -20505,13 +21881,13 @@ sub ipmi_data { $sensors{'fan-psu-2'} = int($row[$i_value]); } if ($extra > 0){ - if ($row[$i_key] =~ /^((MAIN\s|P[_]?)?12V|PSU[12]_VOUT)$/i){ + if ($row[$i_key] =~ /^((MAIN\s|P[_]?)?\+?12V|PSU[12]_VOUT)$/i){ $sensors{'volts-12'} = $row[$i_value]; } - elsif ($row[$i_key] =~ /^(MAIN\s5V|P5V|5VCC|5V PG|5V_SB)$/i){ + elsif ($row[$i_key] =~ /^(MAIN\s5V|P5V|5VCC|5V( PG)?|5V_SB)$/i){ $sensors{'volts-5'} = $row[$i_value]; } - elsif ($row[$i_key] =~ /^(MAIN\s3\.3V|P3V3|3\.3VCC|3\.3V PG|3V3_SB)$/i){ + elsif ($row[$i_key] =~ /^(MAIN\s3\.3V|P3V3|3\.3VCC|3\.3V( PG)?|3V3_SB)$/i){ $sensors{'volts-3.3'} = $row[$i_value]; } elsif ($row[$i_key] =~ /^((P_)?VBAT|CMOS Battery|BATT 3.0V)$/i){ @@ -21616,11 +22992,11 @@ sub proc_data { # In arm/android seen /dev/block/mmcblk0p12 # print "mount: $row->[-1]\n"; if ($row->[-1] !~ /^(nvme[0-9]+n|mmcblk|mtdblk|mtdblock)[0-9]+$/ && - $row->[-1] =~ /[a-z][0-9]+$|dm-[0-9]+$/ && - $row->[-1] !~ /\bloop/ && - !(grep {$row->[-1] =~ /$_$/} (@filters,@mounted)) && - !(grep {$_ =~ /(block\/)?$row->[-1]$/} @mounted) && - !(grep {$_ =~ /^sd[a-z]+$/ && $row->[-1] =~ /^$_[0-9]+/} @mounted)){ + $row->[-1] =~ /[a-z][0-9]+$|dm-[0-9]+$/ && + $row->[-1] !~ /\bloop/ && + !(grep {$row->[-1] =~ /$_$/} (@filters,@mounted)) && + !(grep {$_ =~ /(block\/)?$row->[-1]$/} @mounted) && + !(grep {$_ =~ /^sd[a-z]+$/ && $row->[-1] =~ /^$_[0-9]+/} @mounted)){ $dev_mapped = $dmmapper{$row->[-1]} if $dmmapper{$row->[-1]}; if (@lsblk){ my $id = ($dev_mapped) ? $dev_mapped: $row->[-1]; @@ -22897,7 +24273,7 @@ sub get { set_gtk_data() if $b_gtk && $extra > 1; set_qt_data() if $b_qt && $extra > 1; main::log_data('dump','@desktop', \@desktop) if $b_log; - # ($b_xprop,$kde_session_version,$xdg_desktop,@data,@xprop) = undef; + # ($b_xprop,$kde_session_version,$xdg_desktop,@data,@xprop) = (); eval $end if $b_log; return @desktop; } @@ -23047,8 +24423,8 @@ sub get_env_de_data { foreach my $item (@desktops){ # Check if in xdg_desktop OR desktop_session OR if in $item->[6] and in ps_gui if ((($item->[0] && ($xdg_desktop eq $item->[1] || $desktop_session eq $item->[1])) || - (!$item->[0] && ($xdg_desktop =~ /$item->[1]/ || $desktop_session =~ /$item->[1]/))) || - ($item->[5] && @ps_gui && (grep {/$item->[5]/} @ps_gui))){ + (!$item->[0] && ($xdg_desktop =~ /$item->[1]/ || $desktop_session =~ /$item->[1]/))) || + ($item->[5] && @ps_gui && (grep {/$item->[5]/} @ps_gui))){ ($desktop[0],$desktop[1]) = main::program_data($item->[2]); $b_gtk = $item->[3]; $b_qt = $item->[4]; @@ -23250,101 +24626,58 @@ sub get_ps_de_data { if (@ps_gui){ # the sequence here matters, some desktops like icewm, razor, let you set different # wm, so we want to get the main controlling desktop first - # 1 check program; 2 ps_gui search; 3 data; 4: trigger alternate values/version - my @desktops =( - ['icewm','icewm','icewm'], - ['WindowMaker','WindowMaker','wmaker',''], - ['2bwm','2bwm','2bwm',''],# unverified - ['9wm','9wm','9wm',''], - ['afterstep','afterstep','afterstep',''], - ['aewm++','aewm\+\+','aewm++',''], - ['aewm','aewm','aewm',''], - ['amiwm','amiwm','amiwm',''], - ['antiwm','antiwm','antiwm',''], - ['awesome','awesome','awesome',''], - ['blackbox','blackbox','blackbox',''], - ['bspwm','bspwm','bspwm',''], - ['cagebreak','cagebreak','cagebreak',''], - ['calmwm','calmwm','calmwm',''], - ['catwm','catwm','catwm',''],# unverified - ['clfswm','.*(sh|c?lisp)?.*clfswm','clfswm',''], - ['ctwm','ctwm','ctwm',''], - ['cwm','(openbsd-)?cwm','cwm',''], - ['dwm','dwm','dwm',''], - ['echinus','echinus','echinus',''], - ['evilwm','evilwm','evilwm',''], - ['fireplace','fireplace','fireplace',''], - ['fluxbox','fluxbox','fluxbox',''], - ['flwm','flwm','flwm',''], - ['flwm','flwm_topside','flwm',''], - ['fvwm-crystal','fvwm.*-crystal','fvwm-crystal','fvwm'], - ['fvwm1','fvwm1','fvwm1',''], - ['fvwm2','fvwm2','fvwm2',''], - ['fvwm3','fvwm3','fvwm3',''], - ['fvwm95','fvwm95','fvwm95',''], - ['fvwm','fvwm','fvwm',''], - ['glass','glass','glass',''], - ['hackedbox','hackedbox','hackedbox',''], - ['herbstluftwm','herbstluftwm','herbstluftwm'], - ['instantwm','instantwm','instantwm',''], - ['i3','i3','i3',''], - ['ion3','ion3','ion3',''], - ['jbwm','jbwm','jbwm',''], - ['jwm','jwm','jwm',''], - ['larswm','larswm','larswm',''], - ['leftwm','leftwm','leftwm',''], - ['lwm','lwm','lwm',''], - ['mcwm','mcwm','mcwm',''],# unverified - ['mini','mini','mini',''], - ['musca','musca','musca',''], - ['mvwm','mvwm','mvwm',''], - ['mwm','mwm','mwm',''], - ['nawm','nawm','nawm',''], - ['notion','notion','notion',''], - ['openbox','openbox','openbox',''], - ['orbital','orbital','orbital',''], - ['pekwm','pekwm','pekwm',''], - ['perceptia','perceptia','perceptia',''], - ['penrose','penrose','penrose',''],# unverified - ['qtile','.*(python.*)?qtile','qtile',''], - ['qvwm','qvwm','qvwm',''], - ['ratpoison','ratpoison','ratpoison',''], - ['sawfish','sawfish','sawfish',''], - ['scrotwm','scrotwm','scrotwm',''], - ['snapwm','snapwm','snapwm',''],# unverified - ['spectrwm','spectrwm','spectrwm',''], - ['stumpwm','(sh|c?lisp)?.*stumpwm','stumpwm',''], - ['sway','sway','sway',''], - ['matchbox-window-manager','matchbox-window-manager','matchbox-window-manager',''], - ['tinywm','tinywm','tinywm',''], - ['tvtwm','tvtwm','tvtwm',''], - ['twm','twm','twm',''], - ['uwm','uwm','uwm',''],# unverified - ['waycooler','waycooler','way-cooler',''], - ['way-cooler','way-cooler','way-cooler',''], - ['windowlab','windowlab','windowlab',''], - ['wmfs','wmfs','wmfs',''],# unverified - ['wmfs2','wmfs2','wmfs2',''],# unverified - ['wingo','wingo','wingo',''],# unverified - # not in debian apt, current is wmii, version 3 - ['wmii2','wmii2','wmii2',''], - ['wmii','wmii','wmii',''], - ['wmx','wmx','wmx',''], - ['xmonad','xmonad','xmonad',''], - ## fallback for xfce in case no xprop - ['xfdesktop','xfdesktop','xfdesktop',''], - ['yeahwm','yeahwm','yeahwm',''], - ); - foreach my $item (@desktops){ + # icewm and any other that permits alternate wm to be used need to go first + # in this list. + # unverfied: 2bwm catwm mcwm penrose snapwm uwm wmfs wmfs2 wingo wmii2 + # xfdesktoo is fallback in case not in xprop + my @wms = qw(icewm 2bwm 9wm aewm aewm\+\+ afterstep amiwm antiwm awesome + blackbox bspwm calmwm catwm ctwm dwm echinus evilwm fluxbox fvwm + hackedbox herbstluftwm instantwm i3 ion3 jbwm jwm larswm leftwm lwm + matchbox-window-manager mcwm mini musca mvwm mwm nawm notion + openbox pekwm penrose qvwm ratpoison + sawfish scrotwm snapwm spectrwm tinywm tvtwm twm uwm + windowlab wmfs wmfs2 wingo wmii2 wmii wmx xmonad yeahwm); + my $matches = join('|',@wms) . $wl_compositors; + # note: use my $psg to avoid bizarre return from program_data to ps_gui write + foreach my $psg (@ps_gui){ # no need to use check program with short list of ps_gui - if (grep {/^$item->[1]$/} @ps_gui){ - ($desktop[0],$desktop[1]) = main::program_data($item->[2],$item->[3]); - if ($extra > 1 && $item->[0] eq 'xfdesktop'){ - ($desktop[2],$desktop[3]) = main::program_data('xfdesktop-toolkit',$item->[0],1); + if ($psg =~ /^($matches)$/){ + my $item = $1; + ($desktop[0],$desktop[1]) = main::program_data($item); + if ($extra > 1 && $item eq 'xfdesktop'){ + ($desktop[2],$desktop[3]) = main::program_data('xfdesktop-toolkit',$item,1); } last; } } + if (!$desktop[0]){ + # order matters, these have alternate search patterns from default name + # 1 check program; 2 ps_gui search; 3 data; 4: trigger alternate values/version + @wms =( + ['WindowMaker','WindowMaker','wmaker',''], + ['clfswm','.*(sh|c?lisp)?.*clfswm','clfswm',''], + ['cwm','(openbsd-)?cwm','cwm',''], + ['flwm','flwm','flwm',''], + ['flwm','flwm_topside','flwm',''], + ['fvwm-crystal','fvwm.*-crystal','fvwm-crystal','fvwm'], + ['fvwm1','fvwm1','fvwm1',''], + ['fvwm2','fvwm2','fvwm2',''], + ['fvwm3','fvwm3','fvwm3',''], + ['fvwm95','fvwm95','fvwm95',''], + ['qtile','.*(python.*)?qtile','qtile',''], + ['stumpwm','(sh|c?lisp)?.*stumpwm','stumpwm',''], + ); + foreach my $item (@wms){ + # no need to use check program with short list of ps_gui + if (grep {/^$item->[1]$/} @ps_gui){ + ($desktop[0],$desktop[1]) = main::program_data($item->[2],$item->[3]); + if ($extra > 1 && $item->[0] eq 'xfdesktop'){ + ($desktop[2],$desktop[3]) = main::program_data('xfdesktop-toolkit',$item->[0],1); + } + last; + } + } + } } eval $end if $b_log; } @@ -23407,7 +24740,7 @@ sub get_wm_main { # xprop is set only if not kde/gnome/cinnamon/mate/budgie/lx.. if ($b_xprop){ #KWIN_RUNNING - $wms = 'amiwm|blackbox|bspwm|compiz|kwin_wayland|kwin_x11|kwin|marco|'; + $wms = 'amiwm|blackbox|bspwm|compiz|kwin_wayland|kwin_x11|kwinft|kwin|marco|'; $wms .= 'motif|muffin|openbox|herbstluftwm|twin|ukwm|wm2|windowmaker|i3'; foreach (@xprop){ if (/($wms)/){ @@ -23421,21 +24754,18 @@ sub get_wm_main { main::set_ps_gui() if !$loaded{'ps-gui'}; # order matters, see above logic # due to lisp/python starters, clfswm/stumpwm/qtile will not detect here - $wms = '2bwm|9wm|aewm\+\+|aewm|afterstep|amiwm|antiwm|awesome|blackbox|'; - $wms .= 'cagebreak|calmwm|catwm|clfswm|compiz|ctwm|(openbsd-)?cwm|fluxbox'; - $wms .= '|bspwm|budgie-wm|deepin-wm|dwm|echinus|evilwm|'; - $wms .= 'fireplace|flwm|fvwm-crystal|fvwm1|fvwm2|fvwm3|fvwm95|fvwm|'; - $wms .= 'gala|glass|gnome-shell|hackedbox|i3|instantwm|ion3|jbwm|jwm|'; - $wms .= 'twin|kwin_wayland|kwin_x11|kwin|larswm|leftwm|lwm|'; - $wms .= 'matchbox-window-manager|marco|mcwm|mini|muffin|'; - $wms .= 'musca|deepin-mutter|mutter|deepin-metacity|metacity|mvwm|mwm|'; - $wms .= 'nawm|notion|openbox|orbital|perceptia|qtile|qvwm|'; - $wms .= 'penrose|ratpoison|sawfish|scrotwm|snapwm|spectrwm|'; - $wms .= 'stumpwm|sway|tinywm|tvtwm|twm|ukwm|'; - $wms .= 'way-?cooler|windowlab|WindowMaker|wingo|wm2|wmfs2?|wmii2?|wmx|'; - $wms .= 'xfwm[45]?|xmonad|yeahwm'; - foreach (@ps_gui){ - if (/^($wms)$/){ + my @wms = qw(2bwm 9wm aewm aewm\+\+ afterstep amiwm antiwm awesome blackbox + calmwm catwm clfswm compiz ctwm (openbsd-)?cwm fluxbox bspwm budgie-wm + deepin-wm dwm echinus evilwm flwm fvwm-crystal fvwm1 fvwm2 fvwm3 fvwm95 + fvwm gala gnome-shell hackedbox i3 instantwm ion3 jbwm jwm twin kwin_wayland + kwin_x11 kwinft kwin larswm leftwm lwm matchbox-window-manager marco mcwm mini + muffin musca deepin-mutter mutter deepin-metacity metacity mvwm mwm + nawm notion openbox qtile qvwm penrose ratpoison sawfish scrotwm snapwm + spectrwm stumpwm tinywm tvtwm twm ukwm windowlab WindowMaker wingo wmfs2? + wmii2? wmx xfwm[45]? xmonad yeahwm); + my $wms = join('|',@wms) . $wl_compositors; + foreach my $psg (@ps_gui){ + if ($psg =~ /^($wms)$/){ $working = $1; last; } @@ -23493,14 +24823,16 @@ sub set_info_data { my (@data,@info,$item); my $pattern = 'alltray|awn|bar|bmpanel|bmpanel2|budgie-panel|cairo-dock|'; $pattern .= 'dde-dock|dmenu|dockbarx|docker|docky|dzen|dzen2|'; - $pattern .= 'fancybar|fbpanel|fspanel|glx-dock|gnome-panel|hpanel|i3bar|i3status|icewmtray|'; + $pattern .= 'fancybar|fbpanel|fspanel|glx-dock|gnome-panel|hpanel|'; + $pattern .= 'i3bar|i3status|i3-status-rs|icewmtray|'; $pattern .= 'kdocker|kicker|'; - $pattern .= 'latte|latte-dock|lemonbar|ltpanel|lxpanel|lxqt-panel|'; - $pattern .= 'matchbox-panel|mate-panel|ourico|'; + $pattern .= 'latte|latte-dock|lemonbar|ltpanel|luastatus|lxpanel|lxqt-panel|'; + $pattern .= 'matchbox-panel|mate-panel|nwg-bar|nwg-dock|nwg-panel|ourico|'; $pattern .= 'perlpanel|plank|plasma-desktop|plasma-netbook|polybar|pypanel|'; - $pattern .= 'razor-panel|razorqt-panel|stalonetray|swaybar|taskbar|tint2|trayer|'; - $pattern .= 'ukui-panel|vala-panel|wbar|wharf|wingpanel|witray|'; - $pattern .= 'xfce4-panel|xfce5-panel|xmobar|yabar'; + $pattern .= 'razor-panel|razorqt-panel|rootbar|sfwbar|stalonetray|swaybar|'; + $pattern .= 'taskbar|tint2|trayer|'; + $pattern .= 'ukui-panel|vala-panel|wapanel|waybar|wbar|wharf|wingpanel|witray|'; + $pattern .= 'xfce4-panel|xfce5-panel|xmobar|yabar|yambar'; if (@data = grep {/^($pattern)$/} @ps_gui){ # only one entry per type, can be multiple foreach $item (@data){ @@ -23611,7 +24943,7 @@ sub set { main::log_data('dump','$devices{timer}',$devices{'timer'}); } } - @devices = undef; + undef @devices; eval $end if $b_log; } @@ -23623,7 +24955,7 @@ sub lspci_data { foreach (@data){ # print "$_\n"; if ($device){ - if ($_ =~ /^~$/){ + if ($_ eq '~'){ @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id, $rev,$port,$driver,$modules,$driver_nu,$subsystem,$subsystem_id); assign_data('pci',\@temp); @@ -23756,7 +25088,7 @@ sub pciconf_data { my @data = pci_grabber('pciconf'); foreach (@data){ if ($driver){ - if ($_ =~ /^~$/){ + if ($_ eq '~'){ $vendor = main::clean($vendor); $device = main::clean($device); # handle possible regex in device name, like [ConnectX-3] @@ -23829,12 +25161,12 @@ sub pcidump_data { my @data = pci_grabber('pcidump'); main::set_dboot_data() if !$loaded{'dboot'}; foreach (@data){ - if ($_ =~ /^~$/ && $busid && $device){ + if ($_ eq '~' && $busid && $device){ @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id, $rev,$port,$driver,$modules,$driver_nu,'','','',$serial); assign_data('pci',\@temp); ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id, - $rev,$port,$driver,$modules,$driver_nu,$serial) = undef; + $rev,$port,$driver,$modules,$driver_nu,$serial) = (); next; } if ($_ =~ /^([0-9a-f:]+):([0-9]+):\s([^:]+)$/i){ @@ -23878,12 +25210,12 @@ sub pcictl_data { my @data = pci_grabber('pcictl'); my @data2 = pci_grabber('pcictl-n'); foreach (@data){ - if ($_ =~ /^~$/ && $busid && $device){ + if ($_ eq '~' && $busid && $device){ @temp = ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id, $rev,$port,$driver,$modules,$driver_nu); assign_data('pci',\@temp); ($type,$type_id,$busid,$busid_nu,$device,$vendor_id,$chip_id, - $rev,$port,$driver,$modules,$driver_nu) = undef; + $rev,$port,$driver,$modules,$driver_nu) = (); next; } # it's too fragile to get these in one matching so match, trim, next match @@ -23954,6 +25286,7 @@ sub pci_grabber { # my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/lspci/kot--book-lspci-nnv.txt"; # my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/lspci/steve-mint-topaz-lspci-nnkv.txt"; # my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/lspci/ben81-hwraid-lspci-nnv.txt"; + # my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/lspci/gx78b-lspci-nnv.txt"; # @data = main::reader($file,'strip'); } else { @@ -24033,7 +25366,7 @@ sub soc_devices_files { push(@files,@temp2) if @temp2; @temp2 = main::globber('/sys/devices/*/*/uevent'); # see case 10 push(@files,@temp2) if @temp2; - @temp2 = undef; + undef @temp2; # not sure why, but even as root/sudo, /subsystem|driver/uevent are unreadable with -r test true @files = grep {!/\/(subsystem|driver)\//} @files if @files; main::uniq(\@files); @@ -24054,7 +25387,7 @@ sub soc_devices { $chip_id = $5; $temp = $7; @working = main::reader($file, 'strip') if -r $file; - ($device,$driver,$handle,$type,$vendor_id) = (undef,undef,undef,undef,undef); + ($device,$driver,$handle,$type,$vendor_id) = (); foreach my $data (@working){ @temp2 = split('=', $data); if ($temp2[0] eq 'DRIVER'){ @@ -24453,7 +25786,7 @@ sub set_disklabel_data { last; } else { - my ($b_part,$duid,$part_id,$bytes_sector) = undef; + my ($b_part,$duid,$part_id,$bytes_sector) = (); if ($extra > 2 && $show{'disk'} && $alerts{'fdisk'}->{'action'} eq 'use'){ $disks_bsd{$id}->{'partition-table'} = fdisk_data($id); } @@ -24606,44 +25939,66 @@ sub set_gpart_data { sub get_display_manager { eval $start if $b_log; - my (@data,@found,$path,$working,$b_run,$b_vrun,$b_vrunrc); + my (@data,@found,@glob,$link,$path,@temp); # ldm - LTSP display manager. Note that sddm does not appear to have a .pid - # extension in Arch note: to avoid positives with directories, test for -f - # explicitly, not -e. Guessing on cdm.pid. pcdm uses vt, PCDM-vt9.pid - my @dms = qw(cdm.pid entranced.pid gdm.pid gdm3.pid kdm.pid kdm3.pid ldm.pid - lightdm.pid lxdm.pid mdm.pid nodm.pid pcdm.pid sddm.pid slim.lock - slim.pid tdm.pid udm.pid wdm.pid xdm.pid xenodm.pid); + # extension in Arch. Guessing on cdm, qingy. pcdm uses vt, PCDM-vt9.pid + # Not verified: qingy emptty; greetd.run verified, but alternate: + # greetd-684.sock if no .run seen. Add Ly in case they add run file/directory. + # greetd frontends: agreety dlm gtkgreet qtgreet tuigreet wlgreet + my @dms = qw(cdm emptty entranced gdm gdm3 greetd kdm kdm3 ldm lightdm lxdm ly + mdm nodm pcdm qingy sddm slim tbsm tdm udm wdm xdm xenodm); # these are the only one I know of so far that have version info - my @dms_version = qw(gdm gdm3 lightdm slim); - $b_run = 1 if -d "/run"; - # in most linux, /var/run is a sym link to /run, so no need to check it twice - if (-d "/var/run"){ - my $rdlink = readlink('/var/run'); - $b_vrun = 1 if !$rdlink || ($rdlink && $rdlink ne '/run'); - $b_vrunrc = 1 if -d "/var/run/rc.d"; + my @dms_version = qw(gdm gdm3 lightdm ly slim); + my $pattern = ''; + if (-d '/run'){ + $pattern .= '/run'; } - foreach my $id (@dms){ - # note: $working will create a dir name out of the dm $id, then - # test if pid is in that note: sddm, in an effort to be unique and special, - # do not use a pid/lock file, but rather a random string inside a directory - # called /run/sddm/ so assuming the existence of the pid inside a directory named - # from the dm. Hopefully this change will not have negative results. - $working = $id; - $working =~ s/\.\S+$//; - # note: there were issues with duplicated dm's in inxi, checking @found corrects it - if ((($b_run && (-f "/run/$id" || -d "/run/$working")) || - ($b_vrun && (-f "/var/run/$id" || -d "/var/run/$working")) || - ($b_vrunrc && (-f "/var/run/rc.d/$working" || -d "/var/run/rc.d/$id"))) && - !grep {/$working/i} @found){ - if ($extra > 2 && awk(\@dms_version, $working) && ($path = check_program($working))){} - else {$path = $working;} - # print "$path $extra\n"; - @data = program_data($working,$path,3); - $working = $data[0]; - $working .= ' ' . $data[1] if $data[1]; - push(@found, $working); + # in most linux, /var/run is a sym link to /run, so no need to check it twice + if (-d '/var/run' && ! -l '/var/run'){ + $pattern .= ',' if $pattern; + $pattern .= '/var/run'; + } + if (-d '/var/run/rc.d'){ + $pattern .= ',' if $pattern; + $pattern .= '/var/run/rc.d'; + } + if ($pattern){ + $pattern = '{' . $pattern . '/*}' if $pattern; + # for dm.pid type file or dm directory names, like greetd-684.sock + @glob = globber($pattern) if $pattern; + } + # print Data::Dumper::Dumper \@glob; + # used to test for .pid/lock type file or directory, now just see if the + # search name exists in run and call it good since test would always be true + # if directory existed previously anyway. + if (@glob){ + my $search = join('|',@dms); + @glob = grep {/\/($search)\b/} @glob; + # $search = join('|',@glob); + if (@glob){ + uniq(\@glob); + foreach my $item (@glob){ + my @id = grep {$item =~ /\/$_\b/} @dms; + push(@temp,@id) if @id; + } + # note: there were issues with duplicated dm's, using uniq will handle those + uniq(\@temp) if @temp; } } + @dms = @temp; + # print Data::Dumper::Dumper \@dms; + # we know the files or directories exist so no need for further checks here + foreach my $dm (@dms){ + # do nothing, we just wanted the path + if ($extra > 2 && (grep {$dm eq $_} @dms_version) && + ($path = check_program($dm))){} + else {$path = $dm} + # print "$path $extra\n"; + @data = program_data($dm,$path,3); + $dm = $data[0]; + $dm .= ' ' . $data[1] if $data[1]; + push(@found,$dm); + } if (!@found){ # ly does not have a run/pid file if (grep {$_ eq 'ly'} @ps_gui){ @@ -24719,8 +26074,8 @@ sub get_bsd_os { $distro_id = lc($uname[0]); } if ($distro && - (-e '/etc/pkg/GhostBSD.conf' || -e '/usr/local/etc/pkg/repos/GhostBSD.conf') && - $distro =~ /freebsd/i){ + (-e '/etc/pkg/GhostBSD.conf' || -e '/usr/local/etc/pkg/repos/GhostBSD.conf') && + $distro =~ /freebsd/i){ my $version = (main::grabber("pkg query '%v' os-generic-userland-base 2>/dev/null"))[0]; # only swap if we get result from the query if ($version){ @@ -25010,10 +26365,10 @@ sub system_base_bsd { sub system_base { eval $start if $b_log; - my $base_arch_distro = 'anarchy|antergos|archbang|archlabs|archman|archstrike|arco|artix'; + my $base_arch_distro = 'anarchy|antergos|arch(bang|craft|labs|man|strike)|arco|artix'; # note: arch linux derived distro page claims kaos as arch derived but it is NOT $base_arch_distro .= '|blackarch|bluestar|chakra|ctios|endeavour|garuda|hyperbola|linhes'; - $base_arch_distro .= '|manjaro|mysys2|netrunner\s?rolling|ninja|obarun|parabola'; + $base_arch_distro .= '|mabox|manjaro|mysys2|netrunner\s?rolling|ninja|obarun|parabola'; $base_arch_distro .= '|puppyrus-?a|reborn|snal|talkingarch|ubos'; my $base_debian_version_distro = 'sidux'; my $base_debian_version_osr = '\belive|lmde|neptune|parrot|pureos|rescatux|septor|sparky|tails'; @@ -25045,7 +26400,7 @@ sub system_base { @osr = @osr_working; $system_base = get_os_release(); @osr = @osr_temp if !$system_base; - (@osr_temp,@osr_working) = (undef,undef); + (@osr_temp,@osr_working) = (); } } elsif (-r $base_upstream_lsb){ @@ -25224,7 +26579,7 @@ sub get_os_release { } # note: mint has varying formats here, some have ubuntu as name, 17 and earlier else { - # mint 17 used ubuntu os-release, so won't have $base_version + # mint 17 used ubuntu os-release, so won't have $base_version if ($base_name && $base_version){ $base_id = ubuntu_id($base_version) if $base_type eq 'ubuntu' && $base_version; $base_id = '' if $base_id && "$base_name$base_version" =~ /$base_id/; @@ -25282,8 +26637,9 @@ sub debian_id { return $id; } -# note, these are only for matching derived names, no need to go -# all the way back here, update as new names are known. This is because +# note, these are only for matching distro/mint derived names. +# Update list as new names become available. While first Mint was 2006-08, +# this method depends on /etc/os-release which was introduced 2012-02. # Mint is using UBUNTU_CODENAME without ID data. sub ubuntu_id { eval $start if $b_log; @@ -25291,25 +26647,25 @@ sub ubuntu_id { $codename = lc($codename); my ($id) = (''); my %codenames = ( - 'hirsute' => '21.04', - 'groovy' => '20.10', - 'focal' => '20.04 LTS', - 'eoan' => '19.10', - 'disco' => '19.04', - 'cosmic' => '18.10', - 'bionic' => '18.04 LTS', - 'artful' => '17.10', - 'zesty' => '17.04', - 'yakkety' => '16.10', - 'xenial' => '16.04 LTS', - 'wily' => '15.10', - 'vivid' => '15.04', - 'utopic' => '14.10', - 'trusty' => '14.04 LTS ', - 'saucy' => '13.10', - 'raring' => '13.04', - 'quantal' => '12.10', - 'precise' => '12.04 LTS ', + 'jammy' => '22.04 LTS', + 'impish' => '21.10','hirsute' => '21.04', + 'groovy' => '20.10','focal' => '20.04 LTS', + 'eoan' => '19.10','disco' => '19.04', + 'cosmic' => '18.10','bionic' => '18.04 LTS', + 'artful' => '17.10','zesty' => '17.04', + 'yakkety' => '16.10','xenial' => '16.04 LTS', + 'wily' => '15.10','vivid' => '15.04', + 'utopic' => '14.10','trusty' => '14.04 LTS ', + 'saucy' => '13.10','raring' => '13.04', + 'quantal' => '12.10','precise' => '12.04 LTS ', + # 'oneiric' => '11.10','natty' => '11.04', + # 'maverick' => '10.10','lucid' => '10.04', + # 'karmic' => '9.10','jaunty' => '9.04', + # 'intrepid' => '8.10','hardy' => '8.04', + # 'gutsy' => '7.10','feisty' => '7.04', + # 'edgy' => '6.10', 'dapper' => '6.06', + # 'breezy' => '5.10', 'hoary' => '5.04', + # 'warty' => '4.10', ); $id = $codenames{$codename} if defined $codenames{$codename}; eval $end if $b_log; @@ -25916,9 +27272,10 @@ sub set { # note: for bcache and luks, the device that has that fs is the parent!! if ($show{'logical'}){ $use{'logical-lvm'} = 1 if !$use{'logical-lvm'} && $2 && $2 eq 'lvm'; - if (!$use{'logical-general'} && (($4 && ($4 eq 'crypto_LUKS' || $4 eq 'bcache')) - || ($2 && ($2 eq 'dm' && $1 =~ /veracrypt/i) || - $2 eq 'crypto' || $2 eq 'mpath' || $2 eq 'multipath'))){ + if (!$use{'logical-general'} && (($4 && + ($4 eq 'crypto_LUKS' || $4 eq 'bcache')) || + ($2 && ($2 eq 'dm' && $1 =~ /veracrypt/i) || $2 eq 'crypto' || + $2 eq 'mpath' || $2 eq 'multipath'))){ $use{'logical-general'} = 1; } } @@ -26342,10 +27699,10 @@ sub package_counts { @list = main::globber($_->[3]); } else { - @list = undef; + undef @list; $error = main::message('pm-disabled'); } - $libs = undef; + undef $libs; # print Data::Dumper::Dumper \@list; if (!$error){ $count = scalar @list; @@ -26388,6 +27745,603 @@ sub count_libs { } } +## ParseEDID +{ +package ParseEDID; +# CVT_ratios: +my @known_ratios = qw(5/4 4/3 3/2 16/10 15/9 16/9); +my @edid_info = ( + ['a8', '_header'], + ['a2', 'manufacturer_name'], + + ['v', 'product_code'], + ['V', 'serial_number'], + ['C', 'week'], + ['C', 'year'], + ['C', 'edid_version'], + ['C', 'edid_revision'], + ['a', 'video_input_definition'], + + ['C', 'max_size_horizontal'], # in cm, 0 on projectors + ['C', 'max_size_vertical'], # in cm, 0 on projectors + ['C', 'gamma'], + ['a', 'feature_support'], + ['a10', '_color_characteristics'], + ['a3' , 'established_timings'], + ['a16', 'standard_timings'], + ['a72', 'monitor_details'], + + ['C', 'extension_flag'], + ['C', 'checksum'], +); +my %subfields = ( + manufacturer_name => [ + [1, ''], + [5, '1'], + [5, '2'], + [5, '3'], + ], + video_input_definition => [ + [1, 'digital'], + [1, 'separate_sync'], + [1, 'composite_sync'], + [1, 'sync_on_green'], + [2, ''], + [2, 'voltage_level'], + ], + feature_support => [ + [1, 'DPMS_standby'], + [1, 'DPMS_suspend'], + [1, 'DPMS_active_off'], + [1, 'rgb'], + + [1, ''], + [1, 'sRGB_compliance'], + [1, 'has_preferred_timing'], + [1, 'GTF_compliance'], + ], + # these are VESA timings, basically: VESA-EEDID-A2.pdf + established_timings => [ + # byte 1, 23h + [1, '720x400_70'], + [1, '720x400_88'], + [1, '640x480_60'], + [1, '640x480_67'], + [1, '640x480_72'], + [1, '640x480_75'], + [1, '800x600_56'], + [1, '800x600_60'], + # byte 2, 24h + [1, '800x600_72'], + [1, '800x600_75'], + [1, '832x624_75'], + [1, '1024x768_87i'], + [1, '1024x768_60'], + [1, '1024x768_70'], + [1, '1024x768_75'], + [1, '1280x1024_75'], + # byte 3, 25h + # 7: [1, '1152x870_75'], # apple macII + # 6-0: manufacturer's timings + ], + detailed_timing => [ + [8, 'horizontal_active'], + [8, 'horizontal_blanking'], + [4, 'horizontal_active_hi'], + [4, 'horizontal_blanking_hi'], + [8, 'vertical_active'], + [8, 'vertical_blanking'], + [4, 'vertical_active_hi'], + [4, 'vertical_blanking_hi'], + [8, 'horizontal_sync_offset'], + [8, 'horizontal_sync_pulse_width'], + [4, 'vertical_sync_offset'], + [4, 'vertical_sync_pulse_width'], + [2, 'horizontal_sync_offset_hi'], + [2, 'horizontal_sync_pulse_width_hi'], + [2, 'vertical_sync_offset_hi'], + [2, 'vertical_sync_pulse_width_hi'], + [8, 'horizontal_image_size'], # in mm + [8, 'vertical_image_size'], # in mm + [4, 'horizontal_image_size_hi'], + [4, 'vertical_image_size_hi'], + [8, 'horizontal_border'], + [8, 'vertical_border'], + + [1, 'interlaced'], + [2, 'stereo'], + [2, 'digital_composite'], + [1, 'horizontal_sync_positive'], + [1, 'vertical_sync_positive'], + [1, ''], + ], + # 16 bytes, up to 8 additional timings, each identified by a unique 2 byte + # code derived from the horizontal active pixel count, the image aspect ratio + # and field refresh rate as described in Table 3.19 + standard_timing => [ + [8, 'X'], + [2, 'aspect'], + [6, 'vfreq'], + ], + monitor_range => [ + [8, 'vertical_min'], + [8, 'vertical_max'], + [8, 'horizontal_min'], + [8, 'horizontal_max'], + [8, 'pixel_clock_max'], + ], + manufacturer_specified_range_timing => [ + # http://www.spwg.org/salisbury_march_19_2002.pdf + # for the glossary: http://www.vesa.org/Public/PSWG/PSWG15v1.pdf + [8, 'horizontal_sync_pulse_width_min'], # HSPW (Horizontal Sync Pulse Width) + [8, 'horizontal_sync_pulse_width_max'], + [8, 'horizontal_back_porch_min'], # t_hbp + [8, 'horizontal_back_porch_max'], + [8, 'vertical_sync_pulse_width_min'], # VSPW (Vertical Sync Pulse Width) + [8, 'vertical_sync_pulse_width_max'], + [8, 'vertical_back_porch_min'], # t_vbp (Vertical Back Porch) + [8, 'vertical_back_porch_max'], + [8, 'horizontal_blanking_min'], # t_hp (Horizontal Period) + [8, 'horizontal_blanking_max'], + [8, 'vertical_blanking_min'], # t_vp + [8, 'vertical_blanking_max'], + [8, 'module_revision'], + ], + cea_data_block_collection => [ + [3, 'type'], + [5, 'size'], + ], + cea_video_data_block => [ + [1, 'native'], + [7, 'mode'], + ], +); +my @cea_video_mode_to_detailed_timing = ( + 'pixel_clock', + 'horizontal_active', + 'vertical_active', + 'aspect', + 'horizontal_blanking', + 'horizontal_sync_offset', + 'horizontal_sync_pulse_width', + 'vertical_blanking', + 'vertical_sync_offset', + 'vertical_sync_pulse_width', + 'horizontal_sync_positive', + 'vertical_sync_positive', + 'interlaced' +); +my @cea_video_modes = ( +# [0] pixel clock, [1] X, [2] Y, [3] aspect, [4] Hblank, [5] Hsync_offset, [6] Hsync_pulse_width, +# [7] Vblank, [8] Vsync_offset, [9] Vsync_pulse_width, [10] Hsync+, [11] Vsync+, [12] interlaced +# 59.94/29.97 and similar modes also have a 60.00/30.00 counterpart by raising the pixel clock + [ 25.175, 640, 480, "4/3", 160, 16, 96, 45, 10, 2, 0, 0, 0 ], # 1: 640x 480@59.94 + [ 27.000, 720, 480, "4/3", 138, 16, 62, 45, 9, 6, 0, 0, 0 ], # 2: 720x 480@59.94 + [ 27.000, 720, 480, "16/9", 138, 16, 62, 45, 9, 6, 0, 0, 0 ], # 3: 720x 480@59.94 + [ 74.250, 1280, 720, "16/9", 370, 110, 40, 30, 5, 5, 1, 1, 0 ], # 4: 1280x 720@60.00 + [ 74.250, 1920, 1080, "16/9", 280, 88, 44, 45, 4, 10, 1, 1, 1 ], # 5: 1920x1080@30.00 + [ 27.000, 1440, 480, "4/3", 276, 38, 124, 45, 8, 6, 0, 0, 1 ], # 6: 1440x 480@29.97 + [ 27.000, 1440, 480, "16/9", 276, 38, 124, 45, 8, 6, 0, 0, 1 ], # 7: 1440x 480@29.97 + [ 27.000, 1440, 240, "4/3", 276, 38, 124, 22, 4, 3, 0, 0, 0 ], # 8: 1440x 240@60.05 + [ 27.000, 1440, 240, "16/9", 276, 38, 124, 22, 4, 3, 0, 0, 0 ], # 9: 1440x 240@60.05 + [ 54.000, 2880, 480, "4/3", 552, 76, 248, 45, 8, 6, 0, 0, 1 ], # 10: 2880x 480@29.97 + [ 54.000, 2880, 480, "16/9", 552, 76, 248, 45, 8, 6, 0, 0, 1 ], # 11: 2880x 480@29.97 + [ 54.000, 2880, 240, "4/3", 552, 76, 248, 22, 4, 3, 0, 0, 0 ], # 12: 2880x 240@60.05 + [ 54.000, 2880, 240, "16/9", 552, 76, 248, 22, 4, 3, 0, 0, 0 ], # 13: 2880x 240@60.05 + [ 54.000, 1440, 480, "4/3", 276, 32, 124, 45, 9, 6, 0, 0, 0 ], # 14: 1440x 480@59.94 + [ 54.000, 1440, 480, "16/9", 276, 32, 124, 45, 9, 6, 0, 0, 0 ], # 15: 1440x 480@59.94 + [ 148.500, 1920, 1080, "16/9", 280, 88, 44, 45, 4, 5, 1, 1, 0 ], # 16: 1920x1080@60.00 + [ 27.000, 720, 576, "4/3", 144, 12, 64, 49, 5, 5, 0, 0, 0 ], # 17: 720x 576@50.00 + [ 27.000, 720, 576, "16/9", 144, 12, 64, 49, 5, 5, 0, 0, 0 ], # 18: 720x 576@50.00 + [ 74.250, 1280, 720, "16/9", 700, 440, 40, 30, 5, 5, 1, 1, 0 ], # 19: 1280x 720@50.00 + [ 74.250, 1920, 1080, "16/9", 720, 528, 44, 45, 4, 10, 1, 1, 1 ], # 20: 1920x1080@25.00 + [ 27.000, 1440, 576, "4/3", 288, 24, 126, 49, 4, 6, 0, 0, 1 ], # 21: 1440x 576@25.00 + [ 27.000, 1440, 576, "16/9", 288, 24, 126, 49, 4, 6, 0, 0, 1 ], # 22: 1440x 576@25.00 + [ 27.000, 1440, 288, "4/3", 288, 24, 126, 24, 2, 3, 0, 0, 0 ], # 23: 1440x 288@50.08 + [ 27.000, 1440, 288, "16/9", 288, 24, 126, 24, 2, 3, 0, 0, 0 ], # 24: 1440x 288@50.08 + [ 54.000, 2880, 576, "4/3", 576, 48, 252, 49, 4, 6, 0, 0, 1 ], # 25: 2880x 576@25.00 + [ 54.000, 2880, 576, "16/9", 576, 48, 252, 49, 4, 6, 0, 0, 1 ], # 26: 2880x 576@25.00 + [ 54.000, 2880, 288, "4/3", 576, 48, 252, 24, 2, 3, 0, 0, 0 ], # 27: 2880x 288@50.08 + [ 54.000, 2880, 288, "16/9", 576, 48, 252, 24, 2, 3, 0, 0, 0 ], # 28: 2880x 288@50.08 + [ 54.000, 1440, 576, "4/3", 288, 24, 128, 49, 5, 5, 0, 0, 0 ], # 29: 1440x 576@50.00 + [ 54.000, 1440, 576, "16/9", 288, 24, 128, 49, 5, 5, 0, 0, 0 ], # 30: 1440x 576@50.00 + [ 148.500, 1920, 1080, "16/9", 720, 528, 44, 45, 4, 5, 1, 1, 0 ], # 31: 1920x1080@50.00 + [ 74.250, 1920, 1080, "16/9", 830, 638, 44, 45, 4, 5, 1, 1, 0 ], # 32: 1920x1080@24.00 + [ 74.250, 1920, 1080, "16/9", 720, 528, 44, 45, 4, 5, 1, 1, 0 ], # 33: 1920x1080@25.00 + [ 74.250, 1920, 1080, "16/9", 280, 88, 44, 45, 4, 5, 1, 1, 0 ], # 34: 1920x1080@30.00 + [ 108.000, 2880, 480, "4/3", 552, 64, 248, 45, 9, 6, 0, 0, 0 ], # 35: 2880x 480@59.94 + [ 108.000, 2880, 480, "16/9", 552, 64, 248, 45, 9, 6, 0, 0, 0 ], # 36: 2880x 480@59.94 + [ 108.000, 2880, 576, "4/3", 576, 48, 256, 49, 5, 5, 0, 0, 0 ], # 37: 2880x 576@50.00 + [ 108.000, 2880, 576, "16/9", 576, 48, 256, 49, 5, 5, 0, 0, 0 ], # 38: 2880x 576@50.00 + [ 72.000, 1920, 1080, "16/9", 384, 32, 168, 170, 46, 10, 1, 0, 1 ], # 39: 1920x1080@25.00 + [ 148.500, 1920, 1080, "16/9", 720, 528, 44, 45, 4, 10, 1, 1, 1 ], # 40: 1920x1080@50.00 + [ 148.500, 1280, 720, "16/9", 700, 440, 40, 30, 5, 5, 1, 1, 0 ], # 41: 1280x 720@100.00 + [ 54.000, 720, 576, "4/3", 144, 12, 64, 49, 5, 5, 0, 0, 0 ], # 42: 720x 576@100.00 + [ 54.000, 720, 576, "16/9", 144, 12, 64, 49, 5, 5, 0, 0, 0 ], # 43: 720x 576@100.00 + [ 54.000, 1440, 576, "4/3", 288, 24, 126, 49, 4, 6, 0, 0, 0 ], # 44: 1440x 576@50.00 + [ 54.000, 1440, 576, "16/9", 288, 24, 126, 49, 4, 6, 0, 0, 0 ], # 45: 1440x 576@50.00 + [ 148.500, 1920, 1080, "16/9", 280, 88, 44, 45, 4, 10, 1, 1, 1 ], # 46: 1920x1080@60.00 + [ 148.500, 1280, 720, "16/9", 370, 110, 40, 30, 5, 5, 1, 1, 0 ], # 47: 1280x 720@120.00 + [ 54.000, 720, 480, "4/3", 138, 16, 62, 45, 9, 6, 0, 0, 0 ], # 48: 720x 480@119.88 + [ 54.000, 720, 480, "16/9", 138, 16, 62, 45, 9, 6, 0, 0, 0 ], # 49: 720x 480@119.88 + [ 54.000, 1440, 480, "4/3", 276, 38, 124, 45, 8, 6, 0, 0, 1 ], # 50: 1440x 480@59.94 + [ 54.000, 1440, 480, "16/9", 276, 38, 124, 45, 8, 6, 0, 0, 1 ], # 51: 1440x 480@59.94 + [ 108.000, 720, 576, "4/3", 144, 12, 64, 49, 5, 5, 0, 0, 0 ], # 52: 720x 576@200.00 + [ 108.000, 720, 576, "16/9", 144, 12, 64, 49, 5, 5, 0, 0, 0 ], # 53: 720x 576@200.00 + [ 108.000, 1440, 576, "4/3", 288, 24, 126, 49, 4, 6, 0, 0, 1 ], # 54: 1440x 576@100.00 + [ 108.000, 1440, 576, "16/9", 288, 24, 126, 49, 4, 6, 0, 0, 1 ], # 55: 1440x 576@100.00 + [ 108.000, 720, 480, "4/3", 138, 16, 62, 45, 9, 6, 0, 0, 0 ], # 56: 720x 480@239.76 + [ 108.000, 720, 480, "16/9", 138, 16, 62, 45, 9, 6, 0, 0, 0 ], # 57: 720x 480@239.76 + [ 108.000, 1440, 480, "4/3", 276, 38, 124, 45, 8, 6, 0, 0, 1 ], # 58: 1440x 480@119.88 + [ 108.000, 1440, 480, "16/9", 276, 38, 124, 45, 8, 6, 0, 0, 1 ], # 59: 1440x 480@119.88 + [ 59.400, 1280, 720, "16/9", 2020, 1760, 40, 30, 5, 5, 1, 1, 0 ], # 60: 1280x 720@24.00 + [ 74.250, 1280, 720, "16/9", 2680, 2420, 40, 30, 5, 5, 1, 1, 0 ], # 61: 1280x 720@25.00 + [ 74.250, 1280, 720, "16/9", 2020, 1760, 40, 30, 5, 5, 1, 1, 0 ], # 62: 1280x 720@30.00 + [ 297.000, 1920, 1080, "16/9", 280, 88, 44, 45, 4, 5, 1, 1, 0 ], # 63: 1920x1080@120.00 + [ 297.000, 1920, 1080, "16/9", 720, 528, 44, 45, 4, 10, 1, 1, 0 ], # 64: 1920x1080@100.00 +); +# Exist but IDs Unknown: Pixio, AOpen (AON?), AORUS [probably GBT], Deco Gear, +# Eyoyo, GAEMS, GeChic, KOORUI, Lilliput, Mobile Pixels, Nexanic, +# SunFounder, TECNII, TPEKKA, V7/VSEVEN, +# Guesses: KYY=KYY, MSI=MSI, +# PGS: Princeton Graphic Systems; SDC: Samsung Display Co; +# SIS: Silicon Integrated Systems; STN: Samsung Electronics America; +# TAI: Toshiba America +my %vendors = ( +'ACI' => 'Asus', 'ACR' => 'Acer', 'ACT' => 'Targa', 'ADI' => 'ADI', +'AMW' => 'AMW', 'ANX' => 'Acer Netxix', 'AOC' => 'AOC', 'API' => 'Acer', +'APP' => 'Apple', 'ART' => 'ArtMedia', 'AST' => 'AST Research', 'AUO' => 'AU Optronics', +'BEL' => 'Beltronic', 'BMM' => 'BMM', 'BNQ' => 'BenQ', 'BOE' => 'BOE Display', +'CMN' => 'Chi Mei Innolux', 'CPL' => 'Compal/ALFA', 'CPQ' => 'Compaq', +'CPT' => 'Asus', 'CTX' => 'CTX (Chuntex)', 'CVT' => 'DGM', +'DEC' => 'DEC', 'DEL' => 'Dell', 'DPC' => 'Delta', 'DPL' => 'Digital Projection', 'DWE' => 'Daewoo', +'ECS' => 'Elitegroup', 'EIZ' => 'EIZO', 'EPI' => 'Envision', 'ETR' => 'Rotel', +'FCM' => 'Funai', 'FUS' => 'Fujitsu Siemens', +'GBT' => 'Gigabyte', 'GSM' => 'LG (GoldStar)', 'GWY' => 'Gateway 2000', +'HEI' => 'Hyundai.', 'HIQ' => 'Hyundai ImageQuest', 'HIT' => 'Hitachi', 'HPN' => 'HP', +'HSD' => 'Hannspree', 'HSL' => 'Hansol', 'HTC' => 'Hitachi', 'HVR' => 'Hitache', 'HWP' => 'HP', +'IBM' => 'IBM', 'ICL' => 'Fujitsu ICL', 'IFS' => 'InFocus', 'IQT' => 'Hyundai', +'IVM' => 'Idek Iiyama', +'KDS' => 'KDS', 'KFC' => 'KFC Computek', 'KYY' => 'KYY', +'LEN' => 'Lenovo', 'LGD' => 'LG', 'LKM' => 'Adlas/Azalea', 'LNK' => 'LINK', +'LPL' => 'LG Philips', 'LTN' => 'Lite-On', +'MAG' => 'MAG InnoVision', 'MAX' => 'Maxdata', 'MED' => 'Medion', +'MEI' => 'Panasonic', 'MEL' => 'Mitsubishi', 'MIR' => 'Miro', 'MSI' => 'MSI', 'MTC' => 'MITAC', +'NAN' => 'NANAO', 'NEC' => 'NEC', 'NOK' => 'Nokia', 'NVD' => 'Nvidia', +'OQI' => 'ViewSonic Optiquest', 'ORN' => 'Orion', +'PBN' => 'Packard Bell', 'PCK' => 'Daewoo', 'PDC' => 'Polaroid', 'PGS' => 'Princeton', +'PHL' => 'Philips', 'PIO' => 'Pioneer', 'PNR' => 'Planar', 'PRT' => 'Princeton', +'QDI' => 'Quahtum Data', 'QDS' => 'Quanta Display', 'REL' => 'Relisys', +'SAM' => 'Samsung', 'SAN' => 'Sanyo', 'SDC' => 'Samsung', 'SEC' => 'Seiko Epson', +'SEN' => 'Sensics', 'SHP' => 'Sharp', 'SII' => 'Silicon Image', 'SIS' => 'SIS', +'SMC' => 'Samtron', 'SMI' => 'Smile', 'SNI' => 'Siemens Nixdorf', 'SNY' => 'Sony', +'SPT' => 'Sceptre', 'SRC' => 'Shamrock', 'STN' => 'Samsung', 'STP' => 'Sceptre', +'TAI' => 'Toshiba', 'TAT' => 'Tatung', 'TOS' => 'Toshiba', 'TRL' => 'Royal Information', +'TSB' => 'Toshiba', 'UEG' => 'EliteGroup', 'UNM' => 'Unisys', +'VIT' => 'Visitech', 'VLV' => 'Valve', 'VSC' => 'ViewSonic', 'VTK' => 'Viewteck', +'WTC' => 'Wen Technology', 'ZCM' => 'Zenith', +); +sub _within_limit { + my ($value, $type, $limit) = @_; + $type eq 'min' ? $value >= $limit : $value <= $limit; +} +sub _get_many_bits { + my ($s, $field_name) = @_; + my @bits = split('', unpack('B*', $s)); + my %h; + foreach (@{$subfields{$field_name}}) { + my ($size, $field) = @$_; + my @l = ('0' x (8 - $size), splice(@bits, 0, $size)); + $h{$field} = unpack("C", pack('B*', join('', @l))) if $field && $field !~ /^_/; + } + \%h; +} +sub check_parsed_edid { + my ($edid) = @_; + $edid->{edid_version} >= 1 && $edid->{edid_version} <= 2 or return 'bad edid_version'; + $edid->{edid_revision} != 0xff or return 'bad edid_revision'; + if ($edid->{monitor_range}) { + $edid->{monitor_range}{horizontal_min} && + $edid->{monitor_range}{horizontal_min} <= $edid->{monitor_range}{horizontal_max} + or return 'bad HorizSync'; + $edid->{monitor_range}{vertical_min} && + $edid->{monitor_range}{vertical_min} <= $edid->{monitor_range}{vertical_max} + or return 'bad VertRefresh'; + } + ''; +} +sub _build_detailed_timing { + my ($pixel_clock, $vv) = @_; + my $h = _get_many_bits($vv, 'detailed_timing'); + $h->{pixel_clock} = $pixel_clock / 100; # to have it in MHz + my %detailed_timing_field_size = map { $_->[1], $_->[0] } @{$subfields{detailed_timing}}; + foreach my $field (keys %detailed_timing_field_size) { + $field =~ s/_hi$// or next; + my $hi = delete($h->{$field . '_hi'}); + $h->{$field} += $hi << $detailed_timing_field_size{$field}; + } + $h; +} +sub _add_standard_timing_modes { + my ($edid, $v) = @_; + my @aspect2ratio = ( + $edid->{edid_version} > 1 || $edid->{edid_revision} > 2 ? '16/10' : '1/1', + '4/3', '5/4', '16/9', + ); + $v = [ map { + my $h = _get_many_bits($_, 'standard_timing'); + $h->{X} = ($h->{X} + 31) * 8; + if ($_ ne "\x20\x20" && $h->{X} > 256) { # cf VALID_TIMING in Xorg edid.h + $h->{vfreq} += 60; + if ($h->{ratio} = $aspect2ratio[$h->{aspect}]) { + delete $h->{aspect}; + $h->{Y} = $h->{X} / eval($h->{ratio}); + } + $h; + } else { () } + } unpack('a2' x (length($v) / 2), $v) ]; + $v; +} +sub parse_edid { + my ($raw_edid, $verbose) = @_; + my %edid; + my ($main_edid, @eedid_blocks) = unpack("a128" x (length($raw_edid) / 128), $raw_edid); + my @vals = unpack(join('', map { $_->[0] } @edid_info), $main_edid); + my $i; + foreach (@edid_info) { + my ($field, $v) = ($_->[1], $vals[$i++]); + if ($field eq 'year') { + $v += 1990; + } elsif ($field eq 'manufacturer_name') { + my $h = _get_many_bits($v, 'manufacturer_name'); + $v = join('', map { chr(ord('A') + $h->{$_} - 1) } 1 .. 3); + $v = "" if $v eq "@@@"; + $edid{'manufacturer_name_nice'} = ($v && $vendors{$v}) ? $vendors{$v} : ''; + } elsif ($field eq 'video_input_definition') { + $v = _get_many_bits($v, 'video_input_definition'); + } elsif ($field eq 'feature_support') { + $v = _get_many_bits($v, 'feature_support'); + } elsif ($field eq 'established_timings') { + my $h = _get_many_bits($v, 'established_timings'); + $v = [ + sort { $a->{X} <=> $b->{X} || $a->{vfreq} <=> $b->{vfreq} } + map { /(\d+)x(\d+)_(\d+)(i?)/ ? { X => $1, Y => $2, vfreq => $3, $4 ? (interlace => 1) : () } : () } + grep { $h->{$_} } keys %$h ]; + } elsif ($field eq 'standard_timings') { + $v = _add_standard_timing_modes(\%edid, $v); + } elsif ($field eq 'monitor_details') { + while ($v) { + (my $pixel_clock, my $vv, $v) = unpack("v a16 a*", $v); + if ($pixel_clock) { + # detailed timing + my $h = _build_detailed_timing($pixel_clock, $vv); + push @{$edid{detailed_timings}}, $h + if $h->{horizontal_active} > 1 && $h->{vertical_active} > 1; + } else { + (my $flag, $vv) = unpack("n x a*", $vv); + if ($flag == 0xfd) { + # range + $edid{monitor_range} = _get_many_bits($vv, 'monitor_range'); + if ($edid{monitor_range}{pixel_clock_max} == 0xff) { + delete $edid{monitor_range}{pixel_clock_max}; + } else { + $edid{monitor_range}{pixel_clock_max} *= 10; #- to have it in MHz + } + } elsif ($flag == 0xf) { + my $range = _get_many_bits($vv, 'manufacturer_specified_range_timing'); + my $e = $edid{detailed_timings}[0]; + my $valid = 1; + foreach my $m ('min', 'max') { + my %total; + foreach my $dir ('horizontal', 'vertical') { + $range->{$dir . '_sync_pulse_width_' . $m} *= 2; + $range->{$dir . '_back_porch_' . $m} *= 2; + $range->{$dir . '_blanking_' . $m} *= 2; + if ($e && $e->{$dir . '_active'} + && _within_limit($e->{$dir . '_blanking'}, $m, $range->{$dir . '_blanking_' . $m}) + && _within_limit($e->{$dir . '_sync_pulse_width'}, $m, $range->{$dir . '_sync_pulse_width_' . $m}) + && _within_limit($e->{$dir . '_blanking'} - $e->{$dir . '_sync_offset'} - $e->{$dir . '_sync_pulse_width'}, + $m, $range->{$dir . '_back_porch_' . $m})) { + $total{$dir} = $e->{$dir . '_active'} + $range->{$dir . '_blanking_' . $m}; + } + } + if ($total{horizontal} && $total{vertical}) { + my $hfreq = $e->{pixel_clock} * 1000 / $total{horizontal}; + my $vfreq = $hfreq * 1000 / $total{vertical}; + $range->{'horizontal_' . ($m eq 'min' ? 'max' : 'min')} = _round($hfreq); + $range->{'vertical_' . ($m eq 'min' ? 'max' : 'min')} = _round($vfreq); + } else { + $valid = 0; + } + } + $edid{$valid ? 'monitor_range' : 'manufacturer_specified_range_timing'} = $range; + } elsif ($flag == 0xfa) { + push @{$edid{standard_timings}}, _add_standard_timing_modes(\%edid, unpack('a12', $vv)); + } elsif ($flag == 0xfc) { + my $prev = $edid{monitor_name}; + $edid{monitor_name} = ($prev ? "$prev " : '') . unpack('A13', $vv); + } elsif ($flag == 0xfe) { + push @{$edid{monitor_text}}, unpack('A13', $vv); + } elsif ($flag == 0xff) { + push @{$edid{serial_number2}}, unpack('A13', $vv); + } else { + $verbose && $vv ne "\0" x 13 && $vv ne " " x 13 and + warn "parse_edid: unknown flag $flag\n"; + } + } + } + } + $edid{$field} = $v if $field && $field !~ /^_/; + } + foreach (@eedid_blocks) { + my ($tag, $v) = unpack("C a*", $_); + if ($tag == 0x02) { # CEA EDID + my $dtd_offset; + ($dtd_offset, $v) = unpack("x C x a*", $v); + next if $dtd_offset < 4; + $dtd_offset -= 4; + while ($dtd_offset > 0) { + if (!$v) { + warn "parse_edid: DTD offset outside of available data\n" if $verbose; + last; + } + my $h = _get_many_bits($v, 'cea_data_block_collection'); + $dtd_offset -= $h->{size} + 1; + my $vv; + ($vv, $v) = unpack("x a$h->{size} a*", $v); + if ($h->{type} == 0x02) { # Video Data Block + my @vmodes = unpack("a" x $h->{size}, $vv); + foreach my $vmode (@vmodes) { + $h = _get_many_bits($vmode, 'cea_video_data_block'); + my $cea_mode = $cea_video_modes[$h->{mode} - 1]; + if (!$cea_mode) { + warn "parse_edid: unhandled CEA mode $h->{mode}\n" if $verbose; + next; + } + my %det_mode = (source => 'cea_vdb'); + @det_mode{@cea_video_mode_to_detailed_timing} = @$cea_mode; + push @{$edid{detailed_timings}}, \%det_mode; + } + } + } + while (length($v) >= 18) { + (my $pixel_clock, my $vv, $v) = unpack("v a16 a*", $v); + last if !$pixel_clock; + my $h = _build_detailed_timing($pixel_clock, $vv); + push @{$edid{detailed_timings}}, $h + if $h->{horizontal_active} > 1 && $h->{vertical_active} > 1; + } + } else { + $verbose && warn "parse_edid: unknown tag $tag\n"; + } + } + $edid{max_size_precision} = 'cm'; + $edid{EISA_ID} = $edid{manufacturer_name} . sprintf('%04x', $edid{product_code}) if $edid{product_code} && $edid{manufacturer_name}; + if ($edid{monitor_range}) { + $edid{HorizSync} = $edid{monitor_range}{horizontal_min} . '-' . $edid{monitor_range}{horizontal_max}; + $edid{VertRefresh} = $edid{monitor_range}{vertical_min} . '-' . $edid{monitor_range}{vertical_max}; + } + if ($edid{max_size_vertical}) { + $edid{ratio} = $edid{max_size_horizontal} / $edid{max_size_vertical}; + $edid{ratio_name} = _ratio_name($edid{max_size_horizontal}, $edid{max_size_vertical}, 'cm'); + $edid{ratio_precision} = 'cm'; + } + if ($edid{feature_support}{has_preferred_timing} && $edid{detailed_timings}[0]) { + $edid{detailed_timings}[0]{preferred} = 1; + } + foreach my $h (@{$edid{detailed_timings}}) { + # EDID standard is ambiguous on how interlaced modes should be + # specified; workaround clearly broken modes: + if ($h->{interlaced}) { + foreach ("720x480", "1440x480", "2880x480", "720x576", "1440x576", "2880x576", "1920x1080") { + if ($_ eq $h->{horizontal_active} . 'x' . $h->{vertical_active} * 2) { + $h->{vertical_active} *= 2; + $h->{vertical_blanking} *= 2; + $h->{vertical_sync_offset} *= 2; + $h->{vertical_sync_pulse_width} *= 2; + $h->{vertical_blanking} |= 1; + } + } + } + # if the mm size given in the detailed_timing is not far from the cm size + # put it as a more precise cm size + my %in_cm = ( + horizontal => _define($h->{horizontal_image_size}) / 10, + vertical => _define($h->{vertical_image_size}) / 10, + ); + my ($error) = sort { $b <=> $a } map { abs($edid{'max_size_' . $_} - $in_cm{$_}) } keys %in_cm; + if ($error <= 0.5) { + $edid{'max_size_' . $_} = $in_cm{$_} foreach keys %in_cm; + $edid{max_size_precision} = 'mm'; + } + if ($error < 1 && $in_cm{vertical}) { + # using it for the ratio + $edid{ratio} = $in_cm{horizontal} / $in_cm{vertical}; + $edid{ratio_name} = _ratio_name($in_cm{horizontal}, $in_cm{vertical}, 'mm'); + $edid{ratio_precision} = 'mm'; + } + $h->{bad_ratio} = 1 if + $edid{ratio_precision} && + abs($edid{ratio} - $h->{horizontal_active} / $h->{vertical_active}) > ($edid{ratio_precision} eq 'mm' ? 0.02 : 0.2); + + if ($edid{max_size_vertical}) { + $h->{vertical_dpi} = $h->{vertical_active} / $edid{max_size_vertical} * 2.54; + } + if ($edid{max_size_horizontal}) { + $h->{horizontal_dpi} = $h->{horizontal_active} / $edid{max_size_horizontal} * 2.54; + } + my $dpi_string = ''; + if ($h->{vertical_dpi} && $h->{horizontal_dpi}) { + $dpi_string = + abs($h->{vertical_dpi} / $h->{horizontal_dpi} - 1) < 0.05 ? + sprintf("%d dpi", $h->{horizontal_dpi}) : + sprintf("%dx%d dpi", $h->{horizontal_dpi}, $h->{vertical_dpi}); + } + my $horizontal_total = $h->{horizontal_active} + $h->{horizontal_blanking}; + my $vertical_total = $h->{vertical_active} + $h->{vertical_blanking}; + no warnings 'uninitialized'; + $h->{ModeLine_comment} = sprintf qq(# Monitor %s%s modeline (%.1f Hz vsync, %.1f kHz hsync, %sratio %s%s)), + $h->{preferred} ? "preferred" : "supported", + $h->{source} eq 'cea_vdb' ? " CEA" : '', + $h->{pixel_clock} / $horizontal_total / $vertical_total * 1000 * 1000 * ($h->{interlaced} ? 2 : 1), + $h->{pixel_clock} / $horizontal_total * 1000, + $h->{interlaced} ? "interlaced, " : '', + _nearest_ratio($h->{horizontal_active} / $h->{vertical_active}, 0.01) || sprintf("%.2f", $h->{horizontal_active} / $h->{vertical_active}), + $dpi_string ? ", $dpi_string" : ''; + + $h->{ModeLine} = sprintf qq("%dx%d" $h->{pixel_clock} %d %d %d %d %d %d %d %d %shsync %svsync%s), + $h->{horizontal_active}, $h->{vertical_active}, + + $h->{horizontal_active}, + $h->{horizontal_active} + $h->{horizontal_sync_offset}, + $h->{horizontal_active} + $h->{horizontal_sync_offset} + $h->{horizontal_sync_pulse_width}, + $horizontal_total, + + $h->{vertical_active}, + $h->{vertical_active} + $h->{vertical_sync_offset}, + $h->{vertical_active} + $h->{vertical_sync_offset} + $h->{vertical_sync_pulse_width}, + $vertical_total, + + $h->{horizontal_sync_positive} ? '+' : '-', + $h->{vertical_sync_positive} ? '+' : '-', + $h->{interlaced} ? ' Interlace' : ''; + } + $edid{diagonal_size} = sqrt(_sqr($edid{max_size_horizontal}) + _sqr($edid{max_size_vertical})) / 2.54; + + \%edid; +} +sub _nearest_ratio { + my ($ratio, $max_error) = @_; + my @sorted = + sort { $a->[1] <=> $b->[1] } + map { + my $error = abs($ratio - eval($_)); + $error > $max_error ? () : [ $_, $error ]; + } @known_ratios; + $sorted[0][0]; +} +sub _ratio_name { + my ($horizontal, $vertical, $precision) = @_; + if ($precision eq 'mm') { + _nearest_ratio($horizontal / $vertical, 0.1); + } else { + my $error = 0.5; + my $ratio1 = _nearest_ratio(($horizontal + $error) / ($vertical - $error), 0.2); + my $ratio2 = _nearest_ratio(($horizontal - $error) / ($vertical + $error), 0.2); + $ratio1 && $ratio2 or return; + if ($ratio1 eq $ratio2) { + $ratio1; + } else { + my $ratio = _nearest_ratio($horizontal / $vertical, 0.2); + join(' or ', $ratio, $ratio eq $ratio1 ? $ratio2 : $ratio1); + } + } +} +sub _define { defined $_[0] ? $_[0] : 0 } +sub _sqr { $_[0] * $_[0] } +sub _round { int($_[0] + 0.5) } +} + ## PartitionData - set/get # for /proc/partitions only, see DiskDataBSD for BSD partition data. { @@ -26459,6 +28413,79 @@ sub get_pci_vendor { return $vendor; } +# $rows, $num by ref. +sub get_pcie_data { + eval $start if $b_log; + my ($bus_id,$j,$rows,$num,$type) = @_; + $type ||= ''; + # see also /sys/class/drm/ + my $path_start = '/sys/bus/pci/devices/0000:'; + return if !$bus_id || ! -d $path_start . $bus_id; + $path_start .= $bus_id; + my $path = $path_start . '/{max_link_width,current_link_width,max_link_speed'; + $path .= ',current_link_speed}'; + my @files = globber($path); + if ($type eq 'gpu'){ + $path = $path_start . '/0000*/0000*/{mem_info_vram_used,mem_info_vram_total}'; + push(@files,globber($path)); + } + # print @files,"\n"; + return if !@files; + my (%data,$name); + my %gen = ( + '2.5 GT/s' => 1, + '5 GT/s' => 2, + '8 GT/s' => 3, + '16 GT/s' => 4, + '32 GT/s' => 5, + '64 GT/s' => 6, + ); + foreach (@files){ + if (-r $_){ + $name = $_; + $name =~ s|^/.*/||; + $data{$name} = reader($_,'strip',0); + if ($name eq 'max_link_speed' || $name eq 'current_link_speed'){ + $data{$name} =~ s/\.0\b| PCIe$//g; # trim .0 off in 5.0, 8.0 + } + } + } + # print Data::Dumper::Dumper \%data; + # Maximum PCIe Bandwidth = SPEED * WIDTH * (1 - ENCODING) - 1Gb/s. + if ($data{'current_link_speed'} && $data{'current_link_width'}){ + $$rows[$j]->{key($$num++,1,2,'pcie')} = ''; + if ($b_admin && $gen{$data{'current_link_speed'}}){ + $$rows[$j]{key($$num++,0,3,'gen')} = $gen{$data{'current_link_speed'}}; + } + $$rows[$j]{key($$num++,0,3,'speed')} = $data{'current_link_speed'}; + $$rows[$j]->{key($$num++,0,3,'lanes')} = $data{'current_link_width'}; + if ($b_admin && (($data{'max_link_speed'} && + $data{'max_link_speed'} ne $data{'current_link_speed'}) || + ($data{'max_link_width'} && + $data{'max_link_width'} > $data{'current_link_width'}))){ + $$rows[$j]->{key($$num++,1,3,'link-max')} = ''; + if ($data{'max_link_speed'} && + $data{'max_link_speed'} ne $data{'current_link_speed'}){ + $$rows[$j]{key($$num++,0,4,'gen')} = $gen{$data{'max_link_speed'}}; + $$rows[$j]->{key($$num++,0,4,'speed')} = $data{'max_link_speed'}; + } + if ($data{'max_link_width'} && + $data{'max_link_width'} > $data{'current_link_width'}){ + $$rows[$j]->{key($$num++,0,4,'lanes')} = $data{'max_link_width'}; + } + } + } + if ($type eq 'gpu' && $data{'mem_info_vram_used'} && $data{'mem_info_vram_total'}){ + $$rows[$j]->{key($$num++,1,2,'vram')} = ''; + $$rows[$j]->{key($$num++,0,3,'total')} = get_size($data{'mem_info_vram_total'}/1024,'string'); + my $used = get_size($data{'mem_info_vram_used'}/1024,'string'); + $used .= ' (' . sprintf('%0.1f',($data{'mem_info_vram_used'}/$data{'mem_info_vram_total'}*100)) . '%)'; + $$rows[$j]->{key($$num++,0,3,'used')} = $used; + + } + eval $end if $b_log; +} + sub set_ps_aux { eval $start if $b_log; my ($header,@ps,@temp); @@ -26503,30 +28530,29 @@ sub set_ps_aux { sub set_ps_gui { eval $start if $b_log; $loaded{'ps-gui'} = 1; - my ($working,@match,@temp); + my ($b_wl,$working,@match,@temp); # desktops / wm (some wm also compositors) if ($show{'system'}){ @temp=qw(razor-desktop razor-session lxsession lxqt-session tdelauncher tdeinit_phase1); push(@match,@temp); @temp=qw(2bwm 3dwm 9wm afterstep aewm aewm\+\+ amiwm antiwm awesome - blackbox bspwm - cagebreak calmwm catwm (sh|c?lisp).*clfswm ctwm (openbsd-)?cwm dwm evilwm + blackbox bspwm calmwm catwm (sh|c?lisp).*clfswm ctwm (openbsd-)?cwm dwm evilwm fluxbox flwm flwm_topside fvwm.*-crystal fvwm1 fvwm2 fvwm3 fvwm95 fvwm herbstluftwm i3 icewm instantwm ion3 jbwm jwm larswm leftwm lwm matchbox-window-manager mcwm mini monsterwm musca mwm nawm notion - openbox orbital pekwm penrose perceptia python.*qtile qtile qvwm ratpoison - sawfish scrotwm snapwm spectrwm (sh|c?lisp).*stumpwm sway - tinywm tvtwm twm uwm - waycooler way-cooler windowlab WindowMaker wingo wm2 wmfs wmfs2 wmii2 wmii - wmx - xfdesktop xmonad yeahwm); + openbox pekwm penrose python.*qtile qvwm ratpoison + sawfish scrotwm snapwm spectrwm (sh|c?lisp).*stumpwm + tinywm tvtwm twm uwm windowlab WindowMaker wingo wm2 wmfs wmfs2 wmii2 wmii + wmx xfdesktop xmonad yeahwm); push(@match,@temp); + $b_wl = 1; } - # wm: + # wm: note that for all but the listed wm, the wm and desktop would be the + # same, particularly with all smaller wayland wm/compositors. if ($show{'system'} && $extra > 1){ @temp=qw(budgie-wm compiz deepin-wm gala gnome-shell - twin kwin_wayland kwin_x11 kwin marco + twin kwin_wayland kwin_x11 kwinft kwin marco deepin-metacity metacity metisse mir muffin deepin-mutter mutter ukwm xfwm[45]?); push(@match,@temp); @@ -26538,29 +28564,42 @@ sub set_ps_gui { if ($show{'system'} && $extra > 2){ @temp=qw(alltray awn bar bmpanel bmpanel2 budgie-panel cairo-dock dde-dock dmenu dockbarx docker docky dzen dzen2 - fbpanel fspanel glx-dock gnome-panel hpanel i3bar icewmtray - kdocker kicker latte latte-dock lemonbar ltpanel lxpanel lxqt-panel - matchbox-panel mate-panel ourico + fbpanel fspanel glx-dock gnome-panel hpanel i3bar i3-status(-rs)? icewmtray + kdocker kicker latte latte-dock lemonbar ltpanel luastatus lxpanel lxqt-panel + matchbox-panel mate-panel nwg-bar nwg-dock nwg-panel ourico perlpanel plank plasma-desktop plasma-netbook polybar pypanel - razor-panel razorqt-panel stalonetray swaybar taskbar tint2 trayer - ukui-panel vala-panel wbar wharf wingpanel witray - xfce[45]?-panel xmobar yabar); + razor-panel razorqt-panel rootbar + sfwbar stalonetray swaybar taskbar tint2 trayer + ukui-panel vala-panel wapanel waybar wbar wharf wingpanel witray + xfce[45]?-panel xmobar yambar yabar); push(@match,@temp); } # compositors (for wayland these are also the server, note. # for wayland always show, so always load these - if ($show{'graphic'} && $extra > 0){ - @temp=qw(3dwm asc budgie-wm compiz compton deepin-wm dwc dcompmgr - enlightenment fireplace gnome-shell grefson kmscon kwin_wayland kwin_x11 - liri marco metisse mir moblin motorcar muffin mutter - orbital papyros perceptia picom rustland sommelier sway swc - ukwm unagi unity-system-compositor - wavy waycooler way-cooler wayfire wayhouse westford weston xcompmgr - xfwm[45]?); + if ($show{'graphic'}){ + @temp=qw(3dwm budgie-wm cairo compiz compton deepin-wm dcompmgr enlightenment + gala gnome-shell kmscon kwin_wayland kwin_x11 kwinft kwin + marco metisse mir moblin muffin mutter picom steamcompmgr + ukwm unagi unity-system-compositor wayland xcompmgr xfwm[45]?); push(@match,@temp); + $b_wl = 1; } uniq(\@match); my $matches = join('|', @match); + if ($b_wl){ + # wayland compositors generally are compositors and wm. + # These will be used globally to avoid having to redo it over and over. + $wl_compositors = '|' . join('|',qw(asc awc + cage cagebreak cardboard chameleonwm clayland comfc + dwc dwl epd-wm fireplace feathers fenestra glass gamescope greenfield grefson + hikari hopalong inaban japokwm kiwmi labwc laikawm lipstick liri + mahogany marina maze motorcar newm nucleus orbital perceptia phoc pywm qtile + river rootston rustland simulavr skylight sommelier sway swc swvkc + tabby taiwins tinybox tinywl trinkster velox vimway vivarium + wavy waybox way-?cooler wayfire wayhouse waymonad westeros westford + weston wio\+? wxr[cd] xuake)); + $matches .= $wl_compositors; + } foreach (@ps_cmd){ if (/^(|[\S]*\/)($matches)(\/|\s|$)/){ $working = $2; @@ -26953,8 +28992,8 @@ sub shell_launcher { } # in case sudo starts inxi, parent is shell (or perl inxi if run by debugger) # so: perl (2) started pinxi with sudo (3) in sh (4) in terminal - my $shells = 'ash|bash|busybox|cicada|csh|dash|doas|elvish|fish|fizsh|ksh|ksh93|'; - $shells .= 'lksh|login|loksh|mksh|nash|oh|oil|osh|pdksh|perl|posh|'; + my $shells = 'ash|bash|busybox|cicada|csh|dash|doas|elvish|fish|fizsh|ksh|'; + $shells .= 'ksh93|lksh|login|loksh|mksh|nash|oh|oil|osh|pdksh|perl|posh|'; $shells .= 'su|sudo|tcsh|xonsh|yash|zsh'; $shells .= shell_test('return'); my $i = 0; @@ -27416,7 +29455,7 @@ sub usbconfig_data { undef @working; } elsif (/^([a-z_-]+)([0-9]+)\.([0-9]+):\s+<[^>]+>\s+at usbus([0-9]+)\b/){ - ($class_id,$cfg,$power,$speed,$subclass_id,$type) = undef; + ($class_id,$cfg,$power,$speed,$subclass_id,$type) = (); ($product,$product_id,$vendor,$vendor_id) = ('','','',''); $hub_id = $2; $addr_id = $3; @@ -27925,9 +29964,8 @@ sub assign_usb_type { # nested hubs of course can be > 1 too. No need to build these if none of # lines are showing. if (($row->[4] && $row->[4] eq '09') || - ($row->[14] && $row->[14] eq 'Hub') || - $row->[1] <= 1 || - (!$show{'audio'} && !$show{'bluetooth'} && !$show{'graphic'} && !$show{'network'})){ + ($row->[14] && $row->[14] eq 'Hub') || $row->[1] <= 1 || + (!$show{'audio'} && !$show{'bluetooth'} && !$show{'graphic'} && !$show{'network'})){ return; } $row->[13] = '' if !defined $row->[13]; # product @@ -27937,27 +29975,24 @@ sub assign_usb_type { set_network_regex() if $show{'network'} && !$network_regex; # NOTE: a device, like camera, can be audio+graphic if ($show{'audio'} && ( - (@asound_ids && $row->[7] && (grep {$row->[7] eq $_} @asound_ids)) || - ($row->[14] =~ /Audio/) || - ($row->[15] && $row->[15] =~ /audio/) || - ($row->[13] && lc($row->[13]) =~ /(audio|\bdac[0-9]*\b|headphone|\bmic(rophone)?\b)/))){ + (@asound_ids && $row->[7] && (grep {$row->[7] eq $_} @asound_ids)) || + ($row->[14] =~ /Audio/) || ($row->[15] && $row->[15] =~ /audio/) || + ($row->[13] && lc($row->[13]) =~ /(audio|\bdac[0-9]*\b|headphone|\bmic(rophone)?\b)/))){ push(@{$usb{'audio'}},$row); } - if ($show{'graphic'} && ( - $row->[14] && ($row->[14] =~ /Video/) || - ($row->[15] && $row->[15] =~ /video/) || - ($row->[13] && lc($row->[13]) =~ /(camera|\bdvb-t|\b(pc)?tv\b|video|webcam)/))){ + if ($show{'graphic'} && ($row->[14] && ($row->[14] =~ /Video/) || + ($row->[15] && $row->[15] =~ /video/) || + ($row->[13] && lc($row->[13]) =~ /(camera|\bdvb-t|\b(pc)?tv\b|video|webcam)/))){ push(@{$usb{'graphics'}},$row); } - elsif ($show{'bluetooth'} && ( - $row->[14] && $row->[14] =~ /Bluetooth/ || - ($row->[15] && $row->[15] =~ /\b(btusb|ubt)\b/))){ + elsif ($show{'bluetooth'} && ($row->[14] && $row->[14] =~ /Bluetooth/ || + ($row->[15] && $row->[15] =~ /\b(btusb|ubt)\b/))){ push(@{$usb{'bluetooth'}},$row); } elsif ($show{'network'} && ( - ($row->[14] && $row->[14] =~ /(Ethernet|Network|WiFi)/i) || - ($row->[15] && $row->[15] =~ /(^ipw|^iwl|wifi)/) || - ($row->[13] && $row->[13] =~ /($network_regex)/i))){ + ($row->[14] && $row->[14] =~ /(Ethernet|Network|WiFi)/i) || + ($row->[15] && $row->[15] =~ /(^ipw|^iwl|wifi)/) || + ($row->[13] && $row->[13] =~ /($network_regex)/i))){ # print "$1\n"; push(@{$usb{'network'}},$row); } @@ -28038,7 +30073,7 @@ sub check_type { $type = 'Bluetooth' } elsif (($driver && $driver =~ /video/) || - $name =~ /(camera|display|\bdvb-t|\b(pc)?tv\bvideo|webcam)/){ + $name =~ /(camera|display|\bdvb-t|\b(pc)?tv\bvideo|webcam)/){ $type = 'Video'; } elsif ($name =~ /(wlan|wi-?fi|802\.1[15]|(11|54|108|240|300|450|1300)\s?mbps|(11|54|108|240)g\b|wireless[\s-][gn]\b|wireless.*adapter)/){ @@ -28054,7 +30089,7 @@ sub check_type { $type = 'HID'; } elsif (($driver && $driver =~ /^(umass)$/) || - $name =~ /\b(disk|drive|flash)\b/){ + $name =~ /\b(disk|drive|flash)\b/){ $type = 'Mass Storage'; } return $type; @@ -28600,8 +30635,8 @@ sub system_item { } # don't print the desktop if it's a wm and the same if ($extra > 1 && $desktop_data[5] && - (!$desktop_data[0] || $desktop_data[5] =~ /^(deepin.+|gnome[\s_-]shell|budgie.+)$/i || - index(lc($desktop_data[5]),lc($desktop_data[0])) == -1)){ + (!$desktop_data[0] || $desktop_data[5] =~ /^(deepin.+|gnome[\s_-]shell|budgie.+)$/i || + index(lc($desktop_data[5]),lc($desktop_data[0])) == -1)){ $wm = $desktop_data[5]; $wm .= ' ' . $desktop_data[6] if $extra > 2 && $desktop_data[6]; } diff --git a/inxi.1 b/inxi.1 index e8b0e37..356b2d3 100644 --- a/inxi.1 +++ b/inxi.1 @@ -15,7 +15,7 @@ .\" with this program; if not, write to the Free Software Foundation, Inc., .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. .\" -.TH INXI 1 "2022\-01\-18" "inxi" "inxi manual" +.TH INXI 1 "2022\-02\-22" "inxi" "inxi manual" .SH NAME inxi \- Command line system information script for console and IRC @@ -266,43 +266,49 @@ in order to avoid spamming. ARM CPUs: show \fBfeatures\fR items. .TP .B \-F \fR, \fB\-\-full\fR Show Full output for inxi. Includes all Upper Case line letters (except -\fB\-J\fR and \fB\-W\fR) plus \fB\-\-swap\fR, \fB\-s\fR and \fB\-n\fR. Does -not show extra verbose options such as \fB\-d \-f \-i -J \-l \-m \-o \-p \-r -\-t \-u \-x\fR unless you use those arguments in the command, e.g.: +\fB\-J\fR and \fB\-W\fR) plus \fB\-\-swap\fR, \fB\-s\fR and \fB\-n\fR. Does not +show extra verbose options such as \fB\-d \-f \-i -J \-l \-m \-o \-p \-r \-t \-u +\-x\fR unless you use those arguments in the command, e.g.: \fBinxi \-Frmxx\fR .TP .B \-G \fR, \fB\-\-graphics\fR Show Graphic device(s) information, including details of device and display -drivers (\fBloaded:\fR, and, if applicable: \fBunloaded:\fR, \fBfailed:\fR), -display protocol (if available), display server (and/or Wayland compositor), -vendor and version number, e.g.: +drivers (\fBX:\fR \fBloaded:\fR, and, if applicable: \fBunloaded:\fR, +\fBfailed:\fR, and active \fBgpu:\fR drivers), display protocol (if available), +display server (and/or Wayland compositor), vendor and version number, e.g.: -\fBDisplay: x11 server: Xorg 1.15.1\fR +\fBDisplay: x11 server: Xorg v: 1.15.1\fR + +or + +\fBDisplay: wayland server: X.org v: 1.20.1 with: Xwayland v: 20.1\fR If protocol is not detected, shows: \fBDisplay: server: Xorg 1.15.1\fR -Also shows screen resolution(s) (per monitor/X screen), OpenGL renderer, -OpenGL core profile version/OpenGL version. +Also shows screen resolution(s) (per monitor/X screen). For X.org: OpenGL +renderer, OpenGL core profile version/OpenGL version; for VESA: data (for +Xvesa); for Wayland: GBM/EGL data (not implemented). -Compositor information will show if detected using \fB\-xx\fR option -or always if detected and Wayland. +Compositor information will show if detected using \fB\-xx\fR option or always +if detected and Wayland since the compositor is the server with Wayland. + +\fB\-Gxx\fR shows monitor data as well, if detected. .TP .B \-h \fR, \fB\-\-help\fR -The help menu. Features dynamic sizing to fit into terminal window. Set script -global \fBCOLS_MAX_CONSOLE\fR if you want a different default value, or -use \fB\-y \fR to temporarily override the defaults or actual window -width. +The help menu. Features dynamic sizing to fit into terminal window. Set script +global \fBCOLS_MAX_CONSOLE\fR if you want a different default value, or use +\fB\-y \fR to temporarily override the defaults or actual window width. .TP .B \-i \fR, \fB\-\-ip\fR -Show WAN IP address and local interfaces (latter requires \fBifconfig\fR or -\fBip\fR network tool), as well as network output from \fB\-n\fR. -Not shown with \fB\-F\fR for user security reasons. You shouldn't paste your -local/WAN IP. Shows both IPv4 and IPv6 link IP addresses. +Show WAN IP address and local interfaces (latter requires \fBifconfig\fR or +\fBip\fR network tool), as well as network output from \fB\-n\fR. Not shown with +\fB\-F\fR for user security reasons. You shouldn't paste your local/WAN IP. +Shows both IPv4 and IPv6 link IP addresses. .TP .B \-I \fR, \fB\-\-info\fR @@ -313,9 +319,9 @@ for extra information (init type/version, runlevel, packages). Note: if \fB\-m\fR is used or triggered, the memory item will show in the main Memory: report of \fB\-m\fR, not in \fB\Info:\fR. -Raspberry Pi only: uses \fBvcgencmd get_mem gpu\fR to get gpu RAM amount, -if user is in video group and \fBvcgencmd\fR is installed. Uses -this result to increase the \fBMemory:\fR amount and \fBused:\fR amounts. +Raspberry Pi only: uses \fBvcgencmd get_mem gpu\fR to get gpu RAM amount, if +user is in video group and \fBvcgencmd\fR is installed. Uses this result to +increase the \fBMemory:\fR amount and \fBused:\fR amounts. .TP .B \-j\fR, \fB\-\-swap\fR @@ -327,19 +333,17 @@ To show partition labels or UUIDs (when available and relevant), use with .TP .B \-J \fR, \fB\-\-usb\fR -Show USB data for attached Hubs and Devices. Hubs also show number of ports. -Be aware that a port is not always external, some may be internal, and either -used or unused (for example, a motherboard USB header connector that is not -used). +Show USB data for attached Hubs and Devices. Hubs also show number of ports. Be +aware that a port is not always external, some may be internal, and either used +or unused (for example, a motherboard USB header connector that is not used). Hubs and Devices are listed in order of BusID. BusID is generally in this format: BusID\-port[.port][.port]:DeviceID -Device ID is a number created by the kernel, and has no necessary ordering -or sequence connection, but can be used to match this output to lsusb -values, which generally shows BusID / DeviceID (except for tree view, which -shows ports). +Device ID is a number created by the kernel, and has no necessary ordering or +sequence connection, but can be used to match this output to lsusb values, which +generally shows BusID / DeviceID (except for tree view, which shows ports). Examples: \fBDevice\-3: 4\-3.2.1:2\fR or \fBHub: 4\-0:1\fR @@ -355,31 +359,29 @@ Sample: \fB\-ojpl\fR. .TP .B \-L\fR, \fB\-\-logical\fR -Show Logical volume information, for LVM, LUKS, bcache, etc. Shows -size, free space (for LVM VG). For LVM, shows \fBDevice\-[xx]: VG:\fR -(Volume Group) size/free, \fBLV\-[xx]\fR (Logical Volume). LV shows type, -size, and components. Note that components are made up of either containers -(aka, logical devices), or physical devices. The full report requires -doas/sudo/root. +Show Logical volume information, for LVM, LUKS, bcache, etc. Shows size, free +space (for LVM VG). For LVM, shows \fBDevice\-[xx]: VG:\fR (Volume Group) +size/free, \fBLV\-[xx]\fR (Logical Volume). LV shows type, size, and components. +Note that components are made up of either containers (aka, logical devices), or +physical devices. The full report requires doas/sudo/root. -Logical block devices can be thought of as devices that are made up out -of either other logical devices, or physical devices. inxi does its best -to show what each logical device is made out of. RAID devices form a subset -of all possible Logical devices, but have their own section, \fB\-R\fR. +Logical block devices can be thought of as devices that are made up out of +either other logical devices, or physical devices. inxi does its best to show +what each logical device is made out of. RAID devices form a subset of all +possible Logical devices, but have their own section, \fB\-R\fR. If \fB\-R\fR is used with \fB\-Lxx\fR, \fB\-Lxx\fR will not show RAID -information for LVM RAID devices since it's redundant. If \fB\-R\fR is -not used, a simple RAID line will appear for LVM RAID in \fB\-Lxx\fR. +information for LVM RAID devices since it's redundant. If \fB\-R\fR is not used, +a simple RAID line will appear for LVM RAID in \fB\-Lxx\fR. -\fB\-Lxx\fR also shows all components and devices. Note that since -components can go in many levels, each level per primary component is -indicated by either another 'c', or ends with a 'p' device, the physical -device. The number of c's or p's indicates the depth, so you can see which -component belongs to which. +\fB\-Lxx\fR also shows all components and devices. Note that since components +can go in many levels, each level per primary component is indicated by either +another 'c', or ends with a 'p' device, the physical device. The number of c's +or p's indicates the depth, so you can see which component belongs to which. -\fB\-L\fR shows only the top level components/devices (like \fB\-R\fR). -\fB\-La\fR shows component/device size, maj:min ID, mapped name -(if applicable), and puts each component/device on its own line. +\fB\-L\fR shows only the top level components/devices (like \fB\-R\fR). +\fB\-La\fR shows component/device size, maj:min ID, mapped name (if applicable), +and puts each component/device on its own line. Sample: @@ -945,10 +947,10 @@ configuration item \fBINDENT\fR to make permanent. .TP .B \-\-indents [0\-10]\fR -Change primary wrap mode, second, and -y1 level indents. First indent level only -applied if output width is less than max wrap width (see \fB\-\-max\-wrap\fR). 0 -disables all wrapped indents and all second level indents. Use configuration -item \fBINDENTS\fR to make permanent. +Change primary wrap mode, second, and \fB\-y1\fR level indents. First indent +level only applied if output width is less than max wrap width (see +\fB\-\-max\-wrap\fR). 0 disables all wrapped indents and all second level +indents. Use configuration item \fBINDENTS\fR to make permanent. .TP .B \-\-limit [\-1 \- x]\fR @@ -1085,8 +1087,8 @@ each extra data level. .TP .B \-x \-A\fR -\- Adds (if available and/or relevant) \fBvendor:\fR item, which shows -specific vendor [product] information. +\- Adds (if available and/or relevant) \fBvendor:\fR item, which shows specific +vendor [product] information. \- Adds version/port(s)/driver version (if available) for each device. @@ -1101,9 +1103,9 @@ specific vendor [product] information. \- Adds attached battery powered peripherals (\fBDevice\-[number]:\fR) if detected (keyboard, mouse, etc.). -\- Adds battery \fBvolts:\fR, \fBmin:\fR voltages. Note that if difference -is critical, that is current voltage is too close to minimum voltage, shows -without \fB\-x\fR. +\- Adds battery \fBvolts:\fR, \fBmin:\fR voltages. Note that if difference is +critical, that is current voltage is too close to minimum voltage, shows without +\fB\-x\fR. .TP .B \-x \-C\fR @@ -1195,14 +1197,15 @@ for each HCI ID. .TP .B \-x \-G\fR +\- Adds PCI/USB ID of each device. + \- Adds (if available and/or relevant) \fBvendor:\fR item, which shows specific vendor [product] information. -\- Adds direct rendering status. +\- \fBX.org:\fR Adds direct rendering status. -\- Adds (for single GPU, nvidia driver) screen number that GPU is running on. - -\- Adds PCI/USB ID of each device. +\- \fBX.org:\fR Adds (for single GPU, nvidia driver) screen number that GPU is +running on. .TP .B \-x \-i\fR @@ -1329,6 +1332,8 @@ found for each distribution system base detection. .B \-xx \-A\fR \- Adds vendor:product ID for each device. +\- Adds PCIe speed and lanes item (Linux only, if detected). + .TP .B \-xx \-B\fR \- Adds serial number. @@ -1363,31 +1368,103 @@ For a PCIe 3 NVMe drive, with speed of \fB8 GT/s\fR and \fB4\fR lanes \- Adds (\fBhciconfig \fRonly) LMP subversion (and/or HCI revision if applicable) for each device. +\- Adds PCIe speed and lanes item (Linux only, and if PCIe bluetooth, which is +rare). + .TP .B \-xx \-G\fR +Triggers much more complete Screen/Monitor output. + +\fBX.org:\fR requires \fBxdpyinfo\fR or \fBxrandr\fR, and the advanced per +monitor feature requires \fBxrandr\fR. + +\fBWayland:\fR requires any tool capable of showing monitor and resolution +information. \fBSway\fR has \fBswaymsg\fR, \fBweston\-info\fR or +\fBwayland\-info\fR can show Wayland information on any Wayland compositor, and +\fBwlr\-randr\fR can show Wayland information for any \fBwlroots\fR based +compositor. + +Further note that all references to \fBDisplays\fR, \fBScreens\fR, and +\fBMonitors\fR are referring to the \fBX\fR or \fBWayland\fR technical terms, +not normal consumer usage. + +\fBX.org:\fR 1 \fBDisplay\fR runs 1 or more \fBScreens\fR, and 1 \fBScreen\fR +runs 1 or more \fBMonitors\fR. + +\fBWayland:\fR The \fBDisplay\fR is the primary container, and it can contain 1 +or more \fBMonitors\fR. + \- Adds vendor:product ID of each device. -\- Adds Xorg compositor, if found (always shows for Wayland systems). +\- Adds PCIe speed and lanes item (Linux only, and if PCIe device and detected). -\- For free drivers, adds OpenGL compatibility version number if available. -For nonfree drivers, the core version and compatibility versions are usually -the same. Example: +\- Adds output port IDs, active, off (connected but disabled, like a closed +laptop lid) and empty. Example: + +\fBports: active: DVI\-I\-1,VGA\-1 empty: HDMI\-A\-1\fR + +\- Adds \fBDisplay\fR ID. X.org: the Display running the Screen that runs the +Monitors; Wayland: the Display that runs the monitors. + +\- Adds compositor, if found (always shows for Wayland). + +\- \fBWayland:\fR Adds to \fBDisplay\fR \fBd-rect:\fR if > 1 monitors in +Display. This is the size of the rectangle Wayland creates to situate the +monitors in. + +\- \fBX.org:\fR If available, shows \fBalternate:\fR Xorg drivers. This means a +driver on the default list of drivers Xorg automatically checks for the device, +but which is not installed. For example, if you have \fBnouveau\fR driver, +\fBnvidia\fR would show as alternate if it was not installed. Note that +\fBalternate:\fR does NOT mean you should have it, it's just one of the drivers +Xorg checks to see if is present and loaded when checking the device. This can +let you know there are other driver options. Note that if you have explicitly +set the driver in \fBxorg.conf\fR, Xorg will not create this automatic check +driver list. + +\- \fBXorg:\fR Adds total number of \fBScreens\fR listed for the current +\fBDisplay\fR. + +\- \fBXorg:\fR Adds default \fBScreen\fR ID if Screen (not monitor!) total is +greater than 1. + +\- \fBX.org:\fR Adds \fBScreen\fR line, which includes the ID (\fBScreen: 0\fR) +then \fBs-res\fR (Screen resolution), \fBs\-dpi\fR. Remember, this is an Xorg +\fBScreen\fR, NOT a monitor screen, and the information listed is about the Xorg +Screen! It may at times be the same as a single monitor system, but usually it's +different in some ways. Note that the physical monitor dpi and the Xorg dpi are +not necessarily the same thing, and can vary widely. + +\- Adds \fBMonitor\fR lines. Monitors are a subset of a \fBScreen\fR (X.org) or +\fBDisplay\fR (Wayland), each of which can have one or more monitors. Normally a +dual monitor setup is 2 monitors run by one Xorg Screen/Wayland Display. + +\- \fBpos: [primary,]{position string|row\-col}\fR (X.org: requires +\fBxrandr\fR; Wayland: requires \fBswaymsg\fR [sway], \fBwlr\-randr\fR [wlroots +based compositors], \fBweston\-info\fR / \fBwayland\-info\fR [all]). Uses either +explicit \fBprimary\fR value or +0+0 position if no primary monitor value set. +\fBpos:\fR does not show for single monitor setups, or if no position data was +found. + +Position is text (left, center, center-l, center\-r, right, top, top\-left, +top\-center, top\-right, middle, middle\-c, middle\-r, bottom, bottom\-l, +bottom\-c, bottom\-r) if monitors fit within the following grids: 1x2, 1x3, 1x4, +2x1, 2x2, 2x3, 3x1, 3x2, 3x3. If layout not supported in text, uses +[row\-nu]\-[column\-nu] instead to indicate the monitor's position in its grid. + +The position is based on the upper left corner of each monitor relative to the +grid of monitors that the \fBXorg\fR \fBScreen\fR is composed of. + +\- \fBdiag:\fR monitor screen diagonal in mm (inches). Note that this is the +real monitor size, not the Xorg full Screen diagonal size, which can be quite +different. + +\- For free drivers, adds OpenGL compatibility version number if available. For +nonfree drivers, the core version and compatibility versions are usually the +same. Example: \fBv: 3.3 Mesa 11.2.0 compat\-v: 3.0\fR -\- If available, shows \fBalternate:\fR Xorg drivers. This means a driver on -the default list of drivers Xorg automatically checks for the device, but which -is not installed. For example, if you have \fBnouveau\fR driver, \fBnvidia\fR -would show as alternate if it was not installed. Note that \fBalternate:\fR -does NOT mean you should have it, it's just one of the drivers Xorg checks to -see if is present and loaded when checking the device. This can let you know -there are other driver options. Note that if you have explicitly set the driver -in \fBxorg.conf\fR, Xorg will not create this automatic check driver list. - -\- If available, shows Xorg dpi (\fBs-dpi:\fR) for the active Xorg \fBScreen\fR -(not physical monitor). Note that the physical monitor dpi and the Xorg -dpi are not necessarily the same thing, and can vary widely. - .TP .B \-xx \-I\fR \- Adds init type version number (and rc if present). @@ -1444,6 +1521,8 @@ ROM size if using \fBdmidecode\fR. .B \-xx \-N\fR \- Adds vendor:product ID for each device. +\- Adds PCIe speed and lanes item (Linux only, and if PCIe device and detected). + .TP .B \-xx \-r\fR \- Adds Packages info. See \fB\-Ixx\fR @@ -1536,7 +1615,28 @@ are spinning, no rpm data will show. .TP .B \-xxx \-G\fR -\- Adds, if present, PCI/USB class ID. +\- Adds, if present, Device PCI/USB class ID. + +\- Adds to Device \fBserial:\fR number (if found). + +\- \fBXorg:\fR Adds to \fBScreen:\fR \fBs\-size:\fR and \fBs\-diag:\fR. (Screen +size data requires \fBxdpyinfo\fR). This is the X.org Screen dimensions, NOT the +Monitor size! + +\- Adds to Monitors (if detected) frequency (\fBhz:\fR). + +\- Adds to Monitors (if detected) size (\fBsize: 277x156mm (10.9x6.1")\fR). Note +that this is the real physical monitor size, not the Xorg Screen/Wayland Display +size, which can be quite different (1 Xorg Screen / Wayland Display can for +instance contain two or more monitors). + +\- Adds to Monitors \fBmodes: min: max:\fR (if detected). These are the smallest +and largest monitor modes found, using an inexact method, so might not always be +right. + +\- Adds to Monitors \fBserial:\fR number (if detected). + +\- \fBWayland:\fR Adds to Monitors \fBscale:\fR (if detected). .TP .B \-xxx \-I\fR @@ -1631,6 +1731,10 @@ non\-driver modules found, shows nothing. NOTE: just because it lists a module does NOT mean it is available in the system, it's just something the kernel knows could possibly be used instead. +\- Adds PCIe generation, and, if different than running PCIe generation, speed +or lanes, \fBlink\-max: gen: speed: lanes:\fR (only items different from primary +shown). + .TP .B \-a \-C\fR \- Adds CPU family, model\-id, and stepping (replaces \fBrev\fR of \fB\-Cx\fR). @@ -1758,7 +1862,7 @@ differences shown, like \fBcores:, \fBmin/max:\fR, etc. bogomips: 267823 .fi -\- Adds CPU Vulnerabilities (bugs) as known by your current kernel. Lists by +\- Adds CPU Vulnerabilities (bugs) as known by your current kernel. Lists by \fBType: ... (status|mitigation): ....\fR for systems that support this feature (Linux kernel 4.14 or newer, or patched older kernels). @@ -1774,20 +1878,20 @@ Using \fBsmartctl\fR (requires doas/sudo/root privileges). \- Adds device kernel major:minor number (Linux only). -\- Adds SMART report line: status, enabled/disabled, health, powered on, -cycles, and some error cases if out of range values. Note that for Pre\-fail -items, it will show the VALUE and THRESHOLD numbers. It will also fall back -for unknown attributes that are or have been failing and print out the -Attribute name, value, threshold, and failing message. This way even for -unhandled Attribute names, you should get a solid report for full failure -cases. Other cases may show if inxi believes that the item may be approaching -failure. This is a guess so make sure to check the drive and smartctl full -output to verify before taking any further action. +\- Adds SMART report line: status, enabled/disabled, health, powered on, cycles, +and some error cases if out of range values. Note that for Pre\-fail items, it +will show the VALUE and THRESHOLD numbers. It will also fall back for unknown +attributes that are or have been failing and print out the Attribute name, +value, threshold, and failing message. This way even for unhandled Attribute +names, you should get a solid report for full failure cases. Other cases may +show if inxi believes that the item may be approaching failure. This is a guess +so make sure to check the drive and smartctl full output to verify before taking +any further action. -\- Adds, for USB or other external drives, actual model name/serial if -available, and different from enclosure model/serial, and corrects block -sizes if necessary. Adds in drive temperature for some drives as well, -and other useful data. +\- Adds, for USB or other external drives, actual model name/serial if +available, and different from enclosure model/serial, and corrects block sizes +if necessary. Adds in drive temperature for some drives as well, and other +useful data. .TP .B \-a \-E\fR (\fB\-\-bluetooth\fR) @@ -1795,86 +1899,72 @@ and other useful data. Includes, if available, ACL MTU, SCO MTU, Link policy, Link mode, and Service Classes. +\- Adds PCIe generation, and, if different than running PCIe generation, speed +or lanes, \fBlink\-max: gen: speed: lanes:\fR (only items different from primary +shown. Bluetooth PCIe rare). + .TP .B \-a \-G\fR -Triggers a much more complete Screen/Monitor output on the -\fBDisplay:\fR line of \fB\-G\fR. Note that the -basic feature requires \fBxdpyinfo\fR, and the advanced per monitor -feature requires \fBxrandr\fR. - -No support currently exists for \fBWayland\fR since we so far can find -no documentation or easy methods to extract this information from \fBWayland\fR -compositors. This unfortunate situation may change in the future, hopefully. -However, most \fBWayland\fR systems also come with \fBxwayland\fR, -which should supply the tools necessary for the time being. - -Further note that all references to \fBDisplays\fR, \fBScreens\fR, -and \fBMonitors\fR are referring to the \fBX\fR technical terms, -not normal consumer usage. 1 \fBDisplay\fR runs 1 or more -\fBScreens\fR, and a \fBScreen\fR runs 1 or more \fBMonitors\fR. - -\- Adds \fBDisplay\fR ID, for the Display running the Screen that runs the -Monitors. - -\- Adds total number of \fBScreens\fR listed for the current \fBDisplay\fR. - -\- Adds default \fBScreen\fR ID if Screen (not monitor!) total is greater than -1. - -\- Adds \fBScreen\fR line, which includes the ID (\fBScreen: 0\fR) then -\fBs-res\fR (Screen resolution), \fBs\-dpi\fR, \fBs\-size\fR and \fBs\-diag\fR. -Remember, this is an Xorg \fBScreen\fR, NOT a monitor screen, and the -information listed is about the Xorg Screen! It may at times be the same as a -single monitor system, but usually it's different in some ways. - -\- Adds \fBMonitor\fR ID(s). Monitors are a subset of a Screen, each of which -can have one or more monitors. Normally a dual monitor setup is 2 monitors -run by one Xorg Screen. Each monitor has the following data, if available: - -\- \fBres:\fR resolution in pixels. This is the individual monitor's -reported pixel dimensions. - -\- \fBhz:\fR frequency in Herz, as reported to Xorg. Note that there have been -and may continue to be bugs with how Xorg treats > 1 monitor frequencies. - -\- \fBdpi:\fR dpi (dots per inch), aka, ppi (pixels per inch). This is the -physical screen dpi, which is calculated using the screen dimensions and its -resolution. - -\- \fBsize:\fR size in mm (inches). Note that this is the real monitor size, -not the Xorg Screen size, which can be quite different (1 Xorg Screen can -for instance contain two or more monitors). - -\- \fBdiag:\fR monitor screen diagonal in mm (inches). Note that this is -the real monitor size, not the Xorg full Screen diagonal size, which -can be quite different. - -Sample (with both \fBxdpyinfo\fR and \fBxrandr\fR data available): -.nf -\fBinxi \-aG -Graphics: - .... - Display: x11 server: X.Org 1.20.6 driver: loaded: modesetting - display ID: :0.0 screens: 1 - Screen\-1: 0 s\-res: 2560x1024 s\-dpi: 96 s\-size: 677x271mm (26.7x10.7") - s\-diag: 729mm (28.7") - Monitor\-1: DVI\-I\-0 res: 1280x1024 hz: 60 dpi: 96 - size: 338x270mm (13.3x10.6") diag: 433mm (17") - Monitor\-2: VGA\-0 res: 1280x1024 hz: 60 dpi: 86 - size: 376x301mm (14.8x11.9") diag: 482mm (19") - ....\fR -.fi -\- Adds, if present, possible \fBalternate:\fR kernel modules capable of -driving each \fBDevice\-x\fR (not including the current \fBloaded:\fR). If no +\- Adds, if present, possible \fBalternate:\fR kernel modules capable of driving +each \fBDevice\-x\fR (not including the current \fBloaded:\fR). If no non\-driver modules found, shows nothing. NOTE: just because it lists a module does NOT mean it is available in the system, it's just something the kernel knows could possibly be used instead. +\- Adds PCIe generation, and, if different than running PCIe generation, speed +or lanes, \fBlink\-max: gen: speed: lanes:\fR (only items different from primary +shown). + +\- Adds to Monitors \fBbuilt:\fR, \fBgamma:\fR, \fBratio:\fR (if found). + +X.org sample (with both \fBxdpyinfo\fR and \fBxrandr\fR data available): +.nf +\fBinxi \-aGz +Graphics: + Device\-1: AMD Cedar [Radeon HD 5000/6000/7350/8350 Series] vendor: XFX Pine + driver: radeon v: kernel pcie: gen: 1 speed: 2.5 GT/s lanes: 16 link\-max: + gen: 2 speed: 5 GT/s ports: active: DVI\-I\-1,VGA\-1 empty: HDMI\-A\-1 + bus-ID: 0a:00.0 chip-ID: 1002:68f9 class\-ID: 0300 + Display: x11 server: X.Org v: 1.20.13 compositor: xfwm v: 4.16.1 driver: X: + loaded: modesetting gpu: radeon display\-ID: :0.0 screens: 1 + Screen\-1: 0 s\-res: 2560x1024 s\-dpi: 96 s\-size: 677x270mm (26.7x10.6") + s\-diag: 729mm (28.7") + Monitor\-1: DVI\-I\-1 pos: primary,left model: SyncMaster serial: + built: 2004 res: 1280x1024 hz: 60 dpi: 96 gamma: 1.2 + size: 338x270mm (13.3x10.6") diag: 433mm (17") ratio: 5:4 modes: + max: 1280x1024 min: 720x400 + Monitor\-2: VGA\-1 pos: right model: DELL 1908FP serial: + built: 2008 res: 1280x1024 hz: 60 dpi: 86 gamma: 1.4 + size: 376x301mm (14.8x11.9") diag: 482mm (19") ratio: 5:4 modes: + max: 1280x1024 min: 720x400 + ....\fR +.fi +Wayland sample, with Sway/swaymsg: +.nf +\fBinxi \aGz +Graphics: + Device\-1: Advanced Micro Devices [AMD/ATI] Cedar [Radeon HD + 5000/6000/7350/8350 Series] vendor: XFX Pine + driver: radeon v: kernel pcie: gen: 2 speed: 5 GT/s lanes: 16 ports: + active: DVI\-I\-1,VGA\-1 empty: HDMI\-A\-1 bus\-ID: 0a:00.0 chip\-ID: 1002:68f9 + class\-ID: 0300 + Display: wayland server: Xwayland v: 21.1.4 compositor: sway v: 1.6.1 + driver: gpu: radeon d\-rect: 2560x1024 + Monitor-1: DVI\-I\-1 pos: right model: SyncMaster serial: + built: 2004 res: 1280x1024 hz: 60 dpi: 96 gamma: 1.2 + size: 340x270mm (13.4x10.6") diag: 434mm (17.1") ratio: 5:4 modes: + max: 1280x1024 min: 720x400 + Monitor\-2: VGA-1 pos: primary,left model: DELL 1908FP serial: + res: 1280x1024 hz: 60 gamma: 1.4 dpi: 86 gamma: 1.4 + size: 380x300mm (15.0x11.8") diag: 484mm (19.1") ratio: 5:4 modes: + max: 1280x1024 min: 720x400 + Message: Wayland GBM/EGL data currently not available. +.fi .TP .B \-a \-I\fR -\- Adds Packages, totals, per package manager totals, and number of lib -packages detected per package manager. Also adds detected package managers -with 0 packages listed. Moves to \fBRepos\fR if \fB\-ra\fR. +\- Adds Packages, totals, per package manager totals, and number of lib packages +detected per package manager. Also adds detected package managers with 0 +packages listed. Moves to \fBRepos\fR if \fB\-ra\fR. .nf \fBinxi \-aI @@ -1891,9 +1981,9 @@ which you need when using an unfamiliar machine. .TP .B \-a \-j\fR, \fB\-a \-P\fR [swap], \fB\-a \-P\fR [swap] -\- Adds swappiness and vfs cache pressure, and a message to indicate -if the value is the default value or not (Linux only, and only if available). -If not the default value, shows default value as well, e.g. +\- Adds swappiness and vfs cache pressure, and a message to indicate if the +value is the default value or not (Linux only, and only if available). If not +the default value, shows default value as well, e.g. For \fB\-P\fR per swap physical partition: @@ -1907,19 +1997,23 @@ For \fB\-j\fR row 1 output: .TP .B \-a \-L\fR -\- Expands Component report, shows size / maj-min of components and devices, -and mapped name for logical components. Puts each component/device on its own -line. +\- Expands Component report, shows size / maj-min of components and devices, and +mapped name for logical components. Puts each component/device on its own line. \- Adds maj-min to LV and other devices. .TP .B \-a \-n\fR, \fB\-a \-N\fR, \fB\-a \-i\fR -\- Adds, if present, possible \fBalternate:\fR kernel modules capable of -driving each \fBDevice\-x\fR (not including the current \fBdriver:\fR). If no +\- Adds, if present, possible \fBalternate:\fR kernel modules capable of driving +each \fBDevice\-x\fR (not including the current \fBdriver:\fR). If no non\-driver modules found, shows nothing. NOTE: just because it lists a module does NOT mean it is available in the system, it's just something the kernel knows could possibly be used instead. + +\- Adds PCIe generation, and, if different than running PCIe generation, speed +or lanes, \fBlink\-max: gen: speed: lanes:\fR (only items different from primary +shown). + .TP .B \-a \-o\fR \- Adds device kernel major:minor number (Linux only). @@ -1962,35 +2056,35 @@ varies by OS type. .TP .B \-\-alt 40\fR -Bypass \fBPerl\fR as a downloader option. Priority is: Perl (HTTP::Tiny), -Curl, Wget, Fetch, (OpenBSD only) ftp. +Bypass \fBPerl\fR as a downloader option. Priority is: Perl (HTTP::Tiny), Curl, +Wget, Fetch, (OpenBSD only) ftp. .TP .B \-\-alt 41\fR -Bypass \fBCurl\fR as a downloader option. Priority is: Perl (HTTP::Tiny), -Curl, Wget, Fetch, (OpenBSD only) ftp. +Bypass \fBCurl\fR as a downloader option. Priority is: Perl (HTTP::Tiny), Curl, +Wget, Fetch, (OpenBSD only) ftp. .TP .B \-\-alt 42\fR -Bypass \fBFetch\fR as a downloader option. Priority is: Perl (HTTP::Tiny), -Curl, Wget, Fetch, (OpenBSD only) ftp. +Bypass \fBFetch\fR as a downloader option. Priority is: Perl (HTTP::Tiny), Curl, +Wget, Fetch, (OpenBSD only) ftp. .TP .B \-\-alt 43\fR -Bypass \fBWget\fR as a downloader option. Priority is: Perl (HTTP::Tiny), -Curl, Wget, Fetch, OpenBSD only: ftp +Bypass \fBWget\fR as a downloader option. Priority is: Perl (HTTP::Tiny), Curl, +Wget, Fetch, OpenBSD only: ftp .TP .B \-\-alt 44\fR -Bypass \fBCurl\fR, \fBFetch\fR, and \fBWget\fR as downloader options. This +Bypass \fBCurl\fR, \fBFetch\fR, and \fBWget\fR as downloader options. This basically forces the downloader selection to use \fBPerl 5.x\fR -\fBHTTP::Tiny\fR, which is generally slower than \fBCurl\fR or \fBWget\fR but -it may help bypass issues with downloading. +\fBHTTP::Tiny\fR, which is generally slower than \fBCurl\fR or \fBWget\fR but it +may help bypass issues with downloading. .TP .B \-\-bt\-tool [bt\-adapter|hciconfig|rfkill]\fR -Force the use of the given tool for bluetooth report (\fB\-E\fR). -\fBrfkill\fR does not support mac address data. +Force the use of the given tool for bluetooth report (\fB\-E\fR). \fBrfkill\fR +does not support mac address data. .TP .B \-\-dig\fR @@ -1999,15 +2093,15 @@ dig. Restores default behavior for WAN IP, which is use dig if present. .TP .B \-\-display [:]\fR -Will try to get display data out of X (does not usually work as root user). -Default gets display info from display \fB:0\fR. If you use the format -\fB\-\-display :1\fR then it would get it from display \fB1\fR instead, -or any display you specify. +Will try to get display data out of X (does not usually work as root user). +Default gets display info from display \fB:0\fR. If you use the format +\fB\-\-display :1\fR then it would get it from display \fB1\fR instead, or any +display you specify. Note that in some cases, \fB\-\-display\fR will cause inxi to hang endlessly when running the option in console with Intel graphics. The situation regarding -other free drivers such as nouveau/ATI is currently unknown. It may be that -this is a bug with the Intel graphics driver \- more information is required. +other free drivers such as nouveau/ATI is currently unknown. It may be that this +is a bug with the Intel graphics driver \- more information is required. You can test this easily by running the following command out of X/display server: \fBglxinfo \-display :0\fR @@ -2016,16 +2110,16 @@ If it hangs, \fB\-\-display\fR will not work. .TP .B \-\-dmidecode\fR -Shortcut, legacy. See \fB\-\-force dmidecode\fR. +Shortcut. See \fB\-\-force dmidecode\fR. .TP .B \-\-downloader [curl|fetch|perl|wget]\fR Force inxi to use Curl, Fetch, Perl, or Wget for downloads. .TP -.B \-\-force [colors|dmidecode|hddtemp|lsusb|pkg|usb-sys|vmstat|wmctrl]\fR -Various force options to allow users to override defaults. Values be given -as a comma separated list: +.B \-\-force [colors|dmidecode|hddtemp|lsusb|pkg|usb-sys|wayland|vmstat|wmctrl]\fR +Various force options to allow users to override defaults. Values be given as a +comma separated list: \fBinxi \-MJ --force dmidecode,lsusb\fR @@ -2037,8 +2131,8 @@ redirected output. \- \fBhddtemp\fR \- Force use of hddtemp instead of /sys temp data for disks. -\- \fBlsusb\fR \- Forces the USB data generator to use \fBlsusb\fR as -data source (default). Overrides \fBUSB_SYS\fR in user configuration file(s). +\- \fBlsusb\fR \- Forces the USB data generator to use \fBlsusb\fR as data +source (default). Overrides \fBUSB_SYS\fR in user configuration file(s). \- \fBpkg\fR \- Force override of disabled package counts. Known package managers with non\-resolvable issues: @@ -2048,26 +2142,29 @@ rpm: Due to up to 30 seconds delays executing \fBrpm \-qa \-\-nodigest \-\-nosignature\fR .fi on older hardware (and over 1 second on new hardware with some rpm versions) -package counts are disabled by default because of the unacceptable slowdowns -to execute a simple package list command. +package counts are disabled by default because of the unacceptable slowdowns to +execute a simple package list command. -\- \fBusb-sys\fR \- Forces the USB data generator to use \fB/sys\fR as -data source instead of \fBlsusb\fR (Linux only). +\- \fBusb-sys\fR \- Forces the USB data generator to use \fB/sys\fR as data +source instead of \fBlsusb\fR (Linux only). \- \fBvmstat\fR \- Forces use of vmstat for memory data. -\- \fBwmctrl\fR \- Force \fBSystem\fR item \fBwm\fR to use \fBwmctrl\fR -as data source, override default \fBps\fR source. +\- \fBwayland\fR \- Forces use of Wayland, disables x tools glxinfo, xrandr, +xdpyinfo. + +\- \fBwmctrl\fR \- Force \fBSystem\fR item \fBwm\fR to use \fBwmctrl\fR as data +source, override default \fBps\fR source. .TP .B \-\-hddtemp\fR -Shortcut, legacy. See \fB\-\-force hddtemp\fR. +Shortcut. See \fB\-\-force hddtemp\fR. .TP .B \-\-html\-wan\fR Temporary override of \fBNO_HTML_WAN\fR configuration item. Only use to test -w/wo HTML downloaders for WAN IP. Restores default behavior for WAN IP, which -is use HTML downloader if present and if dig failed. +w/wo HTML downloaders for WAN IP. Restores default behavior for WAN IP, which is +use HTML downloader if present and if dig failed. .TP .B \-\-man\fR @@ -2210,11 +2307,11 @@ believes it is running in an IRC client. .TP .B \-\-usb\-sys\fR -Shortcut, legacy. See \fB\-\-force usb\-sys\fR +Shortcut. See \fB\-\-force usb\-sys\fR .TP .B \-\-usb\-tool\fR -Shortcut, legacy. See \fB\-\-force lsusb\fR +Shortcut. See \fB\-\-force lsusb\fR .TP .B \-\-wan\-ip\-url [URL]\fR @@ -2228,9 +2325,13 @@ Same as configuration value (example): \fBWAN_IP_URL='https://mysite.com/ip.php'\fR +.TP +.B \-\-wayland\fR, \fB\-\-wl\fR +Shortcut. See \fB\-\-force wayland\fR. + .TP .B \-\-wm\fR -Shortcut, legacy. See \fB\-\-force wmctl\fR. +Shortcut. See \fB\-\-force wmctl\fR. .SH DEBUGGING OPTIONS .TP diff --git a/inxi.changelog b/inxi.changelog index fd0ff52..57e3763 100644 --- a/inxi.changelog +++ b/inxi.changelog @@ -1,3 +1,367 @@ +================================================================================ +Version: 3.3.13 +Patch: 00 +Date: 2022-02-22 +-------------------------------------------------------------------------------- +RELEASE NOTES: +-------------------------------------------------------------------------------- + +Just as 3.3.10 > 3.3.11 were a huge set of CPU upgrades, including significant +internal refactors, so too is 3.3.13 a significant Graphics upgrade, featuring +significant upgrades to Wayland (and Xvesa/TinyX!) support, and allowing for +much more granular output controls. The legacy -Ga showing +Display/Screen/Monitors is now split apart, and can now work for some features +in and out of display. + +This upgrade should be of significant interest to any Wayland using distro, as +well as the tiny Xvesa based distros like TinyCore, Slitaz, and Puppy. + +-------------------------------------------------------------------------------- +NOTE TO MAINTAINERS AND PACKAGERS: + +If you had Cpanel::JSON::XS or JSON::XS Perl modules as dependencies, you can +remove those, inxi now can use JSON::PP, which is in Core Modules since Perl +5.14 (unless for some reason your distro removed that module from Core Modules). + +Basically inxi will simply look for whichever of the 3 is installed, and use +that one. + +-------------------------------------------------------------------------------- +KNOWN ISSUES: + +1. The free drivers for xorg like amdgpu, modesetting, alter the the internal +kernel IDs for monitors/gfx device ports, which is somewhat bizarre since the +ideal role of any ID is to be an identifier that always works. Due to this +situation, inxi has to map the kernel ids to the x driver monitor IDs in order +to show the advanced monitor data, like model: mapped: and modes:. This may not +always work as expected since if the mapping fails, the data will fail to match +to the monitors. While not enough data is in to make any conclusions, hoping +that this issue does not exist on Wayland compositors. + +-------------------------------------------------------------------------------- +BUGS: + +1. Not sure if this was a bug, but I believe RAM vendor ID matches would never +have generated results, and might have generated errors. That's corrected as +part of Code fix 1. + +-------------------------------------------------------------------------------- +FIXES: + +1. Tiny indentation level issue, for -Ga, Monitor was not set to be a container +for its data. This would only impact -y 1 or json and xml output cases, and would +be subtle, but it was an oversight. + +2. Small fix for monitor dimensions, failed to switch the mm dimensions for +monitors placed in a vertical, portait mode, instead of standard landscape mode. +Now switches mm x and y if that is detected, which corrects dpi as well. + +3. For Xvesa: + +* Show vesa as display driver, Xvesa == vesa, dugh,lol. + +* Show better Interface and Screen resolution data missing messages. + +* See FIX 5 for adding in display-ID:. + +* Show TinX Xvesa string for server data, not just Xvesa. + +4. For Wayland, which currently has no EGL support in inxi, if no glxinfo +present, show EGL Wayland specific Messsage: for advanced EGL data, not the +generic glxinfo that were shown previously. + +5. Display was relying on xdpyinfo or a Wayland environmental variable to set +display-ID:, now falls back always to $ENV{DISPLAY} if nothing else was found +and that exists. I hadn't realized how much was depending on those x tools, +which many people never had installed in the first place. This also supplies +that for Xvesa as well, which has features that need the Display-ID to use. + +6. Intel family 6, model 17h, supposed to be yorkfield, was penryn, fixed. + +7. Small fix for remove_duplicates, it was not case insensitive so missed things +like DELL Dell in strings. + +8. Failed to detect or get Xfree86 X server version number. + +-------------------------------------------------------------------------------- +ENHANCEMENTS: + +1. Extensive Graphics Upgrades: + +* -Gxx Devices: For some gpus / drivers, show vram total and used for -Gxx. +amdgpu supports this, I believe it's the only one, but don't know for sure. + +* -Gxx Devices: (Linux only): Show active, off (connected but disabled, like a +closed laptop screen with attached moniitor), and empty ports on devices. Not +tested for USB yet. + +* -Gxx Devices: Show device ports (like VGA-1, DVI-I-1, HDMI-A-1), active, off +(off is connected but disabled) and empty (linux only). + +* -G Display/Screen: Removed strict dependency on xdpyinfo to show advanced xorg +screen and display data. Now it will show most of the data if xrandr is +available, and all if xrandr and xdpyinfo are installed. More granular error +messages as well. + +* -G Wayland Display: new type, d-rect: for > 1 monitor Wayland display layouts. +Works roughly the same as Screen: s-res: does, except since Wayland has no +'Screen' concept, that goes into Display. This is sort of a rough algo, +basically it takes either the dimensions of the total of x and y resolutions, or +the greatest x or y resolution found for any monitor, whichever is greater, and +uses that to create the display rectangle resolution composite value. + +* -G Display, Monitors: Extended display tool options from just xrandr to +swaymsg, wlr-xrandr, weston-info, wayland-info. Still nothing on kwin_wayland or +gnome-shell and mutter data. + +*. -S, -G: compositors, full redo of list, now supported: + +asc awc cage cagebreak cardboard chameleonwm clayland comfc dwc dwl epd-wm +fireplace feathers fenestra glass gamescope greenfield grefson hikari hopalong +inaban japokwm kiwmi kwinft labwc laikawm lipstick liri mahogany marina maze +motorcar newm nucleus orbital perceptia phoc pywm qtile river rustland simulavr +skylight sommelier sway swc swvkc tabby taiwins tinybox tinywl trinkster velox +vimway vivarium wavy waybox way-cooler wayfire wayhouse waymonad westeros +westford weston wio+ wio wxrc wxrd xuake + +* -G Enhanced Interfaces/GL item, previously only type OpenGL forX, now has: + + * X - OpenGL, requires glxinfo , same as before. + + * Wayland - EGL, currently no tool available, stub in place. Allegedly this + data can be found but have no idea how or if a tool does that yet + + * Xvesa - Interface: interface type (VBE/GOP). GOP not confirmed, no data + samples; v:, source:, dac: (no idea what it is, show it though), controller:, + and ram: items. + + This is based on TinyX/Xvesa as found in TinyCore, but should work in Slitaz + and Puppy TinyX as well if those projects are still around. + +* -G Display/Screen/Monitor data: Created structures and abstractions that allow +for Wayland/Xorg/Xvesa data, most new features will work with any of these. Or +Arcan, if that actually makes it, and we get data for it. We'll wait on Arcan, +heh. + +* -G Display server: For Xvesa, added type TinyX to server if detected. Added +Xwayland, which was not handled previously. For Xwayland, if wayland running, +and if Xorg also installed, shows: + + server: X.org + v: 1.20.14 + with: Xwayland + v: 21.01 + +Otherwise shows: + + server: Xwayland + v: 21.01 + +* -G Compositors: fixed a long standing weak spot, if > 1 compositor detected +running, not common, but could happen, shows all detected compositors. + + Display: x11 + server: X.Org + v: 1.20.13 + compositors: + 1: Mutter + v: 41.1 + 2: xfwm + v: 4.16.1 + driver: + X: + loaded: modesetting + gpu: radeon + +* -G drivers: now shows if X or gpu driver, in each its own section. This makes +it more obvious what is going on: + + Display: x11 + server: X.Org + v: 1.20.13 + driver: + X: + loaded: modesetting + gpu: radeon + resolution: + +* -Gxx Monitors: Show primary monitor with pos: primary,right. Uses either +xrandr 'primary' value, or if no 'primary' found in an Xorg Screen, uses +0+0 +positioned monitor. Position is based on the row and column number in the +rectangular grid of monitors when monitors per Xorg Screen are > 1. + +For most common multi-monitor layouts, text positions are used, which are in +general more clear and easy to understand than their internal numeric +counterparts, that is, unless the layout is too complicated, it will show left, +or top-left, instead of 1-1, and so on. + +Text mode positions are available for the following grid styles currently: 1x2, +1x3, 1x4, 2x1, 2x2, 2x3, 3x1, 3x2, 3x3. 'top' means the top row if > 1 row, +'bottom' means the bottom row, 'middle' is the middle row if 3 rows, 'left' is +the first column, 'right' the last, 'center' if 3 columns, and 'center-l' (1-2), +'center-r' (1-3) are the 2 center columns if 4 columns. 'bottom-l', 'bottom-c', +'bottom-r'; 'middle-l', 'middle-c', 'middle-r'; 'top-left', 'top-center', +'top-right' complete the possible values. + +If the grid of monitors is greater than the supported rows or columns, it will +switch to numeric row-column mode, with column-row numbering starting at 1-1, +top left. + +* -Gxx Monitors: show (if detected, Linux only) monitor model, and if the +display ID (from Xorg or Wayland) is different from the /sys monitor ID, show +mapped: to show the /sys id. + +* -Gxxx Monitors: show modes: max: XxY min: XxY, or mode: XxY (if only 1 mode +found). Shows hz: + +* -Ga Monitors: shows serial, built year, gamma, ratio, if detected. + +2. Added impish 21-10 and jammy 22-04 to ubuntu id. That's for Mint base ID. Not +huge point in updating if Mint doesn't update inxi, but there it is. + +3. For -Axx, -Exx, -Gxx, -Nxx, shows PCIe speed and lanes. With -a, shows max +speed / lanes if different than current speeds/lanes. Note that for unknown +reasons not all devices in a PCIe slot show this data. + +4. -Ixx: terminals added: foot, ate + +5. -Sxx: login/display managers added: emptty, greetd, qingy, tbsm. See CODE 5 +for more info on how this change was done. + +6. -Sxxx: status/dock/panel bars added: i3-status-rs, luastatus, nwg-bar, +nwg-dock, nwg-panel, rootbar, sfwbar, wapanel, waybar, yambar + +7. Added a Tyan board IPMI sensor data set. + +8. Added support for fruid_print for Elbrus -M Machine data. Those boards don't +have dmi tables, but do ship with Elbrus OS which has fruid_print. + +9. More disk vendors! Yes, you know the drill, the world turns, and with every +turn, a flock of new vendors appears, like baby rabbits emerging from their +warren, endlessly, a stream that is the life essence itself... or something. + +-------------------------------------------------------------------------------- +CHANGES: + +1. When xdpyinfo is not installed, user will still see advanced -Ga Monitor and +Screen data as long as xrandr at least is installed. Better error messages as +well now to explain which tool or tools missing caused the missing data. + +2. -Gxx will show basic Screen and Monitors, id, mapped, pos:, model, res, dpi, +diag; -Gxxx adds Monitor modes; --Ga adds screen/monitor size, Screen diag. + +3. -ba/-v2 no longer show the full screens/monitor report, now it remains basic +mode output, which it should have always done, unless -G is also explicitly +added. + +4. Split apart x-server version to v:, which should always have been the case. + +5. Xvesa and Wayland no longer show glxinfo messages for no glxinfo for GL data. +Now they show their own custom messages, appropriate to the case. + +6. json features now test for JSON::PP, JSON::XS, or Cpanel::JSON::XS modules, +and use whichever is found. Note I did not realize JSON::PP was in core modules +as of 5.14 so that makes sense to use, and will allow inxi to start using json +data sources, which are a lot easier to parse. + +7. Changed -G drivers to show subsections for X and gpu drivers, and updated +missing driver messages to account for this change. X drivers now show the sub +sets of loaded/unloaded/failed/alternate, and gpu shows active gpu drivers, +assuming such are detected. + +-------------------------------------------------------------------------------- +DOCUMENTATION: + +1. Help and man page updates for -G Display/Screen/Monitor changes. Redid -G, +-Gx, -Gxx, -Gxxx, -Ga. Added monitor layout position feature. + +2. Updated -Ga for xrandr/xdpyinfo changes. + +3. Updated --recommends to more accurately show function of xdpyinfo and xrandr +for -G and -Ga. + +4. Reorganized and added complete table of contents to docs/inxi-data.txt + +-------------------------------------------------------------------------------- +CODE: + +1. Slightly optimized use of array loads on disk_vendor() and ram_vendor() based +on how it's now done for monitor layouts, which is more efficient, use a scalar +to hold a reference to the array, that avoids having the array ever exist in +more than 1 place. Part of the ongoing process of avoiding extra hash and array +copies globally. + +2. Moved to consistent undef behaviors. + * For lists of variables use () to undefine, changed all of the the following: + 1. (@a,$b,$c,%d) = (undef,undef,undef,undef); + 2. (@a,$b,$c,%d) = (undef); + 3. (@a,$b,$c,%d) = undef; + to use: (@a,$b,$c,%d) = (); This undefines all the variables in the list. + Note that assigning undef to @a in the first example creates an array of 1 + key, with the value undef, and (@a,@b) = (undef,undef) creates arrays of 2 + indexes, or something like that. Not what was wanted. + Examples 2 and 3 assign undef to @a: an array of 1 index, value undef, and + undefine the others variables in the list. This was not the desired behavior! + * For most scalars, arrays, and hashes, use: undef @a; undef $s; undef %h. + * For some hash and array index values, use $h{a} = undef. These cases may want + the key itself to exist, with the value of undef, though I believe: + undef $h{a}; + is synonymous, but still have to verify that. + +I did some testing, and realized that some of the undef I had used in the +various previous ways of using undef were not actually resulting in the expected +behaviors. + +3. Refactored display_data_x into 3 functions, added display_data_xdpyinfo and +display_data_xrandr, which allows for more granular handling of those +dependencies, now inxi can show most advanced display data with only xrandr +installed. + +4. Significantly improved all error handling and missing data for Wayland/Xorg. + +5. Refactored get_display_manager() to better handle corner case dm file or +directory names, and to avoid endless loops. Much cleaner now. Required because +greetd had varying file names, greetd.run, or just greet-546.sock. With some +other dm's that use similar, or unreadable directories in /run, now just doing +a glob of /run/ /var/run, /var/run/rc.d as detected and checking for the dms +in the names, then just using the dms that were found. Simpler. + +6. Massively simplified and integrated compositor logic in Graphics, now using +program_values() and program_data() as appropriate, and simple matching list +to ps_gui data to get detected compositor[s], much simpler, far more efficient +code, less to maintain. Also fixed long-standing weak spot of exiting on first +detected compositor, now shows all detected, with version etc for each if +available. + +7. With 6. also significantly simplified and optimized get_ps_de_data() for +desktop data, that's the ps aux fallback case for wm desktop detections. + +8. Made $wl compositors list global to avoid having to update each section, +that's now used in -G compositor, -S desktop/wm, and wm sections. It is set +in ps_gui() on initial load. + +7. Settled on one and only way to do multiline conditionals, now use no space, +use same indent level as starting if/elsif etc. I've been debating this one, but +can't find any real way to handle that elegantly so I think best to just not +try, and leave it up the code flow to show when it's wrapped condition tests. + +8. Refactored previous gl_output, expanded it to handle all interface types, +OpenGL, EGL (not currently active due to no known tool to get EGL data for +Wayland, and Interface: VBE type data for Xvesa. This roughly completed the +breaking apart of the X.org centric logic for Display, Monitors, and GL data, +and make all sections now fully agnostic to display server or protocol type. + +Should new display servers appear, it will now be far more simple to add support +for them, since they would just plug into the existing abstraction layers. + +9. Added --debug-arg to allow for passing specific custom args to the debugger. + +10. Refactored display_server version, now works much better, creates lists of +server/version, and xwayland as well if found. + +-------------------------------------------------------------------------------- +-- Harald Hope - Tue, 22 Feb 2022 15:30:24 -0800 + ================================================================================ Version: 3.3.12 Patch: 00