How to query X11 display resolution? - linux

It seems like an simple problem, but I can't find the answer: How do you query (via X11) what monitors exist and their resolutions?

Check out display macros and screen macros from the Xlib manual.
Specifically:
From the first link: ScreenCount(), ScreenOfDisplay()
From the second link: WidthOfScreen(), HeightOfScreen()

This might be helpfull for cli and scripting
xwininfo -root
But xRandR might be more accurate, especially, when there is multiple monitor environment:
xrandr

If Xinerama is in use, try XineramaQueryScreens. Otherwise, you may be able to assume a single screen and use (X)WidthOfScreen/(X)HeightOfScreen.
(Also see the other answer. It's remotely possible someone is using the old X screen model where your screens are :x.0, :x.1, etc.)

For modern X servers, there's also the XRandR extension, which provides the most up-to-date model of multi screen layout information, including overlapping screens and dynamic screen changes.
Documentation of it is available in the XRandR 1.3.1 Protocol spec and the libXrandr man page.

The library X11 working only with unix-like OS, so it is a not cross-platform solution.
A full code
#include <stdio.h>
#include <X11/Xlib.h>
int
main(const int argc, const char *argv[])
{
Display *display;
Screen *screen;
// open a display
display = XOpenDisplay(NULL);
// return the number of available screens
int count_screens = ScreenCount(display);
printf("Total count screens: %d\n", count_screens);
for (int i = 0; i < count_screens; ++i) {
screen = ScreenOfDisplay(display, i);
printf("\tScreen %d: %dX%d\n", i + 1, screen->width, screen->height);
}
// close the display
XCloseDisplay(display);
return 0;
}
A compilation
gcc -o setup setup.c -std=c11 `pkg-config --cflags --libs x11`
A result (actual for my computer)
Total count screens: 1
Screen 1: 1366X768
Based on:
https://tronche.com/gui/x/xlib/display/opening.html
https://tronche.com/gui/x/xlib/display/display-macros.html
https://tronche.com/gui/x/xlib/display/screen-information.html
https://stackoverflow.com/a/1829747/6003870

Python
import os
from Xlib import X, display
d = display.Display()
s = d.screen().root
output = os.popen("xrandr --listmonitors | grep '*' | awk {'print $4'}").read().splitlines()
num_sc = s.xinerama_get_screen_count().screen_count
width = s.get_geometry().width
height = s.get_geometry().height
print("Total count screens: %s" % num_sc)
for i in range(num_sc):
print("\tScreen %s(%s): %sX%s" % (i, output[i], width, height))
Bash
$ xrandr --listmonitors
$ xrandr
$ xrandr | grep '*' | awk {'print $1'}

Clean xrandr output for imagemagick use
$ xrandr |grep \* |awk '{print $1}'
Results here in:
1920x1080

The program xdpyinfo tells you almost everything about your X11 server.
$ xdpyinfo | sed -n '/^screen #0/{n;p}'
dimensions: 3840x2160 pixels (696x391 millimeters)
For ffmpeg:
$ xdpyinfo | sed -n '/^screen #0/{n;s/^[ a-z:]*//;s/ .*//p}'
3840x2160

Related

Problem using 2-level pipe when the first program doesn't exit in BASH

Consider this :
xinput --test 11 | grep "button press 1"
*11 is my Optical mouse index (could be anything else) and "button press 1" means left click.
When I click somewhere in the screen, that shows me this :
button press 1
No problem so far . But when I wanted to use the output of that as the input to my C program , I noticed that the stdin of the program after another level of pipe is always empty :
xinput --test 11 | grep "button press 1" | ./poll_test
Here's my poll_test code:
/*This is just a program to test polling functionality on stdin.
I've used other programs instead of this as well.
None of them were able to read the stdin */
#include <fcntl.h>
#include <stdio.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <unistd.h>
void main(int argc, char ** argv) {
int fd;
char buf[1024];
int bytes_read;
struct pollfd pfds[1];
while (1) {
pfds[0].fd = 0;
pfds[0].events = POLLIN;
poll(pfds, 1, -1);
if (pfds[0].revents & POLLIN) {
bytes_read = read(0, buf, 1024);
printf("%s\n" , buf);
if (!bytes_read) {
printf("stdin closed\n");
return;
}
write(1, buf, bytes_read);
}
}
}
It prints nothing despite of the clicks.
That is confusing.This is not a normal behavior. When I run this for example:
ls | grep a | grep b
It shows me the results successfully. The only difference is that the ls here exits after it prints out to the stdout but that's not the case in the xinput version.
I spend a lot of time to write a script to play a beep in the event of a mouse click but that didn't work. So I wanted to use a C program because there's no polling functionality in bash.
As far as I know , working of the pipes in bash is something like this :
The second program (the right one in the pipe statement) gets executed until it wants to READ from its stdin and it stops going further until there's something to read from the STDIN.
With that in mind, the third program in the command I posted should be able to read the output.As it's the case when the first programs exits.
The next step would be to use libxcb directly instead of the command xinput if pipe problem doesn't work.
I'm totally confused. Any help would be much appreciated.
EDIT: I also tried using an intermediate file descriptor:
exec 3<&1
xinput --test 11 | grep -i "button press 1" >&3 | ./poll_test 3>&1
but didn't help.And also flushing the stdout forcibly doesn't work either :
xinput --test 11 | grep -i "button press 1" ; stdbuf -oL | ./poll_test
It seems grep is changing its buffering behaviour depending on whether the output is a terminal or not. I don't know exactly why this happens, but --line-buffered forces it to use line buffering (evaluating the expression as soon as the line ends):
xinput --test 11 | grep "button press 1" --line-buffered | ./poll_test

How to get Windowid from PID from a electron Application

I want to get the WindowID from the PID of an Electron Process (for example riot-desktop)
I tried to get it with xdotools like this:
$ xdotool search --pid $(pgrep riot)
nothing is printed
$ pgrep riot
30461
$ xdotool search --pid 30461
nothing is printed again
You have to search for the pid of the subprocess of electron.
You can get the pid of the subprocess with:
PID=$(ps h -C electron | grep riot | cut -f1 -d"?")
now you can search for the pid
xdotool search --pid $PID
you can combine both commands to a single command
xdotool search --pid $(ps h -C electron | grep riot | grep witzerstorfer | cut -f1 -d"?")
if your ps returns multiple pid's you have to add more grep commands, for example if you work with profiles the command could look like this:
PID=$(ps h -C electron | grep riot | grep $PROFILENAME | cut -f1 -d"?")
If you want to find windowID of your electron application then you can use
win.getMediaSourceId()
Returns String - Window id in the format of DesktopCapturerSource's id. For example "window:1324:0".
More precisely the format is window:id:other_id where id is HWND on Windows, CGWindowID (uint64_t) on macOS and Window (unsigned long) on Linux. other_id is used to identify web contents (tabs) so within the same top level window.
Example:
// In the main process.
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ width: 800, height: 600 })
// Load a remote URL
win.loadURL('https://github.com')
// Or load a local HTML file
win.loadURL(`file://${__dirname}/app/index.html`)
// Print window id
console.log('window id is', win.getMediaSourceId())

prstat in Ubuntu or Centos

As the Java Performance said:
Solaris prstat has additional capabilities
such as reporting both user and kernel or system CPU utilization along with other
microstate information using the prstat -m and -L options. The -m option prints
microstate information, and -L prints statistics on per lightweight process.
There is any tool available like prstat in Centos or Ubuntu ?
I believe the Linux commands you are looking for are top and pstree .
Here is ptree for Linux,
#!/bin/sh
# Solaris style ptree
[ -x /usr/bin/ptree ] && exec /usr/bin/ptree "$#"
# Print process tree
# $1 = PID : extract tree for this process
# $1 = user : filter for this (existing) user
# $1 = user $2 = PID : do both
PATH=/bin:/usr/bin:/usr/sbin:/sbin
export PATH
psopt="-e"
case $1 in
[a-z]*) psopt="-u $1";shift;;
esac
[ -z "$1" ] &&
exec ps $psopt -Ho pid=,args=
#some effort to add less to the ps list
tmp=/tmp/ptree.$$
trap 'rm $tmp' 0 HUP INT TERM
ps $psopt -Ho pid=,args= >$tmp
<$tmp awk '
{ ci=index(substr($0,7),$2); o[ci]=$0 }
ci>s[a] { s[++a]=ci }
$1==pid {
for(i=1;i<=a;i++) {
si=s[i]; if(si<=ci) print o[si]
}
walkdown=ci
next
}
ci<walkdown { exit }
walkdown!=0 { print }
' pid="$1"
There is no prstat "equivalent" tool in Linux. You can use a combination of top and ps (or /proc/$pid/ resources) to get some useful result; maybe writing a shell script (using grep, sed and awk) which collects results from above commands and files.
Just for reference I found this link about top command and kernel, user and idle CPU utilization intresting
http://blog.scoutapp.com/articles/2015/02/24/understanding-linuxs-cpu-stats
Hope this helps.

Control executable in bash script

First of all, I don't know how to search what I want to do.
I have one exec that produces outputs in a terminal (Linux).
Let's take a simple C program a.out:
#include <stdio.h>
int main (int argc, char *argv[]) {
int i=0;
float j=0;
for(i=0; i<=10000000;i++)
{
j = i*-1e-5;
printf (" %d 2.0 %f 4.0 5.0\n",i,j);
}
}
Outputs produced are like :
0 2.0 -0.000000 4.0 5.0
1 2.0 -0.000010 4.0 5.0
2 2.0 -0.000020 4.0 5.0
3 2.0 -0.000030 4.0 5.0
...
Depending on this outputs I want to :
Launch this exec
"Capture" outputs
If 3rd column value reach -0.5, stop/kill exec
How will you do this ?
For instance, exec is not stopped with this script exec.sh:
#/bin/sh
PROG=./a.out
$PROG > output &
progpid=$!
(tail -fn 0 output & echo $! > tailpid ) | awk -v progpid=$progpid '{
if($3<=-0.5){
system("kill "progpid)
# system( ##update other file )
system("kill $(<tailpid)")
}
}'
Any ideas ?
Thanks in advance
I think this sort of construct addresses all of your points:
programname > output &
progpid=$!
(tail -fn 0 output & echo $! > tailpid ) | awk -v progpid=$progpid '{
if( condition ) {
system("kill "progpid)
system( ##update other file )
system("kill $(<tailpid)")
}
}'
We run the program in the background and redirect output to output. Then we monitor output as it is updated using the tail -f option, which reads lines from the end of the file as they are added. Then we pipe this into awk, which can run a system command to kill the program process if condition is met, then run another command to update your parameters in your separate text file, then run another command to kill tail so that it doesn't hang in the background forever (awk will also exit once tail is killed).

Linux retrieve monitor names

Situation: I'm using multiple monitors and I want to get their names in bash. Currently I'm using Ubuntu 10.04.
I know about xrandr. From it I can get only statistics data. What I want is to read all monitor names in an array to work with them.
Is there a clear way to do that without cutting names from some kind of string? A clear way would be reading them from file. A not clear way would be to pipe xrandr output to some sort a function to cut names out from it.
Inspired by Beni's answer, this will read the EDID data using xrandr and extract the monitors names according to the EDID specification, with no need of any external tools like parse-edid:
#!/bin/bash
while read -r output hex conn; do
[[ -z "$conn" ]] && conn=${output%%-*}
echo "# $output $conn $(xxd -r -p <<< "$hex")"
done < <(xrandr --prop | awk '
!/^[ \t]/ {
if (output && hex) print output, hex, conn
output=$1
hex=""
}
/ConnectorType:/ {conn=$2}
/[:.]/ && h {
sub(/.*000000fc00/, "", hex)
hex = substr(hex, 0, 26) "0a"
sub(/0a.*/, "", hex)
h=0
}
h {sub(/[ \t]+/, ""); hex = hex $0}
/EDID.*:/ {h=1}
END {if (output && hex) print output, hex, conn}
' | sort
)
Uses awk to precisely extract the monitor name only, and no extra garbage from the EDID, hence "magic numbers" like 000000fc00, 26 and 0a. Finally uses xxd to convert from hex to ASCII, printing one monitor name per line.
Based on this solution I made a handy script to switch monitors, which can also be used to simply list monitor info:
$ monitor-switch --list
Connected monitors:
# DFP5 HDMI HT-R391
# DFP7 DVI-I DELL U2412M
$ monitor-switch --list
Connected monitors:
# DisplayPort-1 DisplayPort DELL U2412M
# DisplayPort-3 DisplayPort DELL U2415
# HDMI-A-2 HDMI LG TV
Tested on Ubuntu 16.04, 18.04. (I know its too late to answer but this solution is relevant today)
$ sudo apt-get install -y hwinfo
...
$ hwinfo --monitor --short
monitor:
SONY TV
AUO LCD Monitor
I have two monitors attached. One with the laptop and the other is an external display. As soon as the external monitor is plugged-in or out, this command reflects the change. You continuously need to poll. Removing the --short option gives more detailed information.
You can poll the state with the following background job:
$ while true;
> do
> hwinfo --monitor --short;
> sleep 2;
> done >> monitor.log &
The while true loop runs infinite times. The sleep 2 pauses each iteration of the loop for 2 seconds. And the output of hwinfo --monitor --short is appended to monitor.log. This log file can give you the activity history of monitor plug-in and plug-out.
FYI: I am using a background (daemon) python script using the above command (and other similar ones) to detect if someone is doing some HW plug-ins and plug-outs with the systems in the computer lab. If so, I get appropriate notifications that someone plugged-out/in a monitor, mouse or keyboard in almost real-time!
More info about hwinfo command is here. Its man page is also a good source.
sudo get-edid didn't work for me. (EDIT: now works on another computer, Lubuntu 14.10; I'd blame BIOS differences but that's a random guess...)
Anyway under X, xrandr --verbose prints the EDID block. Here is a quick and dirty way to extract it and pass to parse-edid:
#!/bin/bash
xrandr --verbose | perl -ne '
if ((/EDID(_DATA)?:/.../:/) && !/:/) {
s/^\s+//;
chomp;
$hex .= $_;
} elsif ($hex) {
# Use "|strings" if you dont have read-edid package installed
# and just want to see (or grep) the human-readable parts.
open FH, "|parse-edid";
print FH pack("H*", $hex);
$hex = "";
}'
If you don't want to parse xrandr output, write a C program using libXrandr that gets only what you want. If all you want to do is to query information, it can be done quickly. Read this document.
If you want to get the real monitor name, an alternative to #dtmilano's solution is to get the EDID property of the monitor using libXrandr and then manually parse it and print (read the EDID specification).
xrandr source code.
I know this is a dirty way, but it gives me some monitor model name even better than sudo get-edid|parse-edid. It reads information in arrays, and outputs it in a way that can be read like you would read a file. You may modify it according to your needs.
#!/bin/bash
#
#
# get-monitors.sh
#
# Get monitor name and some other properties of connected monitors
# by investigating the output of xrandr command and EDID data
# provided by it.
#
# Copyright (C) 2015,2016 Jarno Suni <8#iki.fi>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. See <http://www.gnu.org/licenses/gpl.html>
set -o nounset
set -o errexit
# EDID format:
# http://en.wikipedia.org/wiki/Extended_Display_Identification_Data#EDID_1.3_data_format
# http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf
declare -r us=';' # separator string;
# If EDID has more than one field with same tag, concatenate them,
# but add this string in between.
declare -r fs=$'\x1f' # Field separator for internal use;
# must be a character that does not occur in data fields.
declare -r invalid_edid_tag='--bad EDID--'
# If base EDID is invalid, don't try to extract information from it,
# but assign this string to the fields.
# Get information in these arrays:
declare -a outs # Output names
declare -a conns # Connection type names (if available)
declare -a names # Monitor names (but empty for some laptop displays)
declare -a datas # Extra data; may include laptop display brand name
# and model name
declare -i no # number of connected outputs (to be counted)
# xrandr command to use as a source of information:
declare -r xrandr_output_cmd="xrandr --prop"
hex_to_ascii() {
echo -n "$1" | xxd -r -p
}
ascii_to_hex() {
echo -n "$1" | xxd -p
}
get_info() {
no=0
declare OIFS=$IFS;
IFS=$fs
while read -r output conn hexn hexd; do
outs[no]="${output}"
conns[no]="${conn}"
names[no]="$(hex_to_ascii "$hexn")"
datas[no]="$(hex_to_ascii "$hexd")"
(( ++no ))
done < <(eval $xrandr_output_cmd | gawk -v gfs="$fs" '
function print_fields() {
print output, conn, hexn, hexd
conn=""; hexn=""; hexd=""
}
function append_hex_field(src_hex,position,app_hex, n) {
n=substr(src_hex,position+10,26)
sub(/0a.*/, "", n)
# EDID specification says field ends by 0x0a
# (\n), if it is shorter than 13 bytes.
#sub(/(20)+$/, "", n)
# strip whitespace at the end of ascii string
if (n && app_hex) return app_hex sp n
else return app_hex n
}
function get_hex_edid( hex) {
getline
while (/^[ \t]*[[:xdigit:]]+$/) {
sub(/[ \t]*/, "")
hex = hex $0
getline
}
return hex
}
function valid_edid(hex, a, sum) {
if (length(hex)<256) return 0
for ( a=1; a<=256; a+=2 ) {
# this requires gawk
sum+=strtonum("0x" substr(hex,a,2))
# this requires --non-decimal-data for gawk:
#sum+=sprintf("%d", "0x" substr(hex,a,2))
}
if (sum % 256) return 0
return 1
}
BEGIN {
OFS=gfs
}
/[^[:blank:]]+ connected/ {
if (unprinted) print_fields()
unprinted=1
output=$1
}
/[^[:blank:]]+ disconnected/ {
if (unprinted) print_fields()
unprinted=0
}
/^[[:blank:]]*EDID.*:/ {
hex=get_hex_edid()
if (valid_edid(hex)) {
for ( c=109; c<=217; c+=36 ) {
switch (substr(hex,c,10)) {
case "000000fc00" :
hexn=append_hex_field(hex,c,hexn)
break
case "000000fe00" :
hexd=append_hex_field(hex,c,hexd)
break
}
}
} else {
# set special value to denote invalid EDID
hexn=iet; hexd=iet
}
}
/ConnectorType:/ {
conn=$2
}
END {
if (unprinted) print_fields()
}' sp=$(ascii_to_hex $us) iet=$(ascii_to_hex $invalid_edid_tag))
IFS="$OIFS"
}
get_info
# print the colums of each display quoted in one row
for (( i=0; i<$no; i++ )); do
echo "'${outs[i]}' '${conns[i]}' '${names[i]}' '${datas[i]}'"
done
You may try ddcprobe and/or get-edid
$ sudo apt-get install xresprobe read-edid
$ sudo ddcprobe
$ sudo get-edid
You're looking for EDID information, which is passed along an I²C bus and interpreted by your video driver. As dtmilano says, get-edit from ddcprobe should work.
You can also get this information by logging your X start:
startx -- -logverbose 6
Years ago, I used a package called read-edid to gather this information.
The read-edid package may be available in Ubuntu already, according to this blog post from 2009.

Resources