Skip to content

Instantly share code, notes, and snippets.

@caiorss
Created October 26, 2020 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caiorss/639df76864d014ead12936fbd361be73 to your computer and use it in GitHub Desktop.
Save caiorss/639df76864d014ead12936fbd361be73 to your computer and use it in GitHub Desktop.
Xlib X11 query windows information
cmake_minimum_required(VERSION 3.9)
project(X11-Xtoll-Project)
#========== Global Configurations =============#
#----------------------------------------------#
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE ON)
find_package(X11 REQUIRED)
#========== Targets Configurations ============#
add_executable( x11-tool x11-tool.cpp )
target_link_libraries( x11-tool ${X11_LIBRARIES} )
#include <iostream>
#include <string>
#include <cstring>
#include <cassert>
// ------ Unix Headers (needed by read_symlink) ------//
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <signal.h>
// ------ X11 Headers ------//
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <cstdlib>
std::string read_symlink (std::string const& path);
int x11_get_property_int32(const char* property_name, Display* dpy, Window root_window);
std::string x11_get_property_str (const char* property_name, Display* dpy, Window root_window);
template<typename Window_Consumer>
void x11_iterate_child_windows( Display* dpy
, Window root_window
, Window_Consumer&& consumer);
std::string x11_window_name(Display* dpy, Window wnd);
int x11_window_get_pid(Display* dpy, Window wnd);
bool x11_show_window_attributes(Display* dpy, Window wnd);
void x11_show_window_properties(Display* dpy, Window wnd);
int main(int argc, char** argv)
{
// Print booleans as 'true' or 'false' instead of 1 or 0
std::cout << std::boolalpha;
Display* dpy = XOpenDisplay(nullptr);
// Placeholder for future error checking in non-prototype code.
assert( dpy != nullptr && "Cannot open display" );
const char* disp_str = XDisplayString(dpy);
assert( disp_str != nullptr && "Failed to get display string" );
std::cout << " [INFO] X11 Display string = " << disp_str << '\n';
// Get root window
Window root = DefaultRootWindow(dpy);
//assert( root != nullptr && "Failed to get root window");
if(argc < 2){
std::cerr << " [USAGE] $ " << argv[0] << " <COMMAND> " << '\n';
return EXIT_FAILURE;
}
auto command = std::string{argv[1]};
// List all windows and their window ids (unique identifiers)
if(command == "list")
{
x11_iterate_child_windows(dpy, root,[](Display* dpy, Window wnd, int index)
{
if( !x11_show_window_attributes(dpy, wnd)) { return; }
x11_show_window_properties(dpy, wnd);
});
return EXIT_SUCCESS;
}
if(command == "winfo")
{
// WARNING: Throws std::invalid_argument exception on parsing failure.
int window_id = std::stoi(argv[2]);
x11_show_window_attributes(dpy, window_id);
x11_show_window_properties(dpy, window_id);
return EXIT_SUCCESS;
}
// Kill window given its unique identifier (window id)
if(command == "kill")
{
// WARNING: Throws std::invalid_argument exception on parsing failure.
int window_id = std::stoi(argv[2]);
// Attempt to get window process ID
int pid = x11_window_get_pid(dpy, window_id);
if(pid < 0){
std::cerr << " [ERROR] Unable to determine window process ID" << '\n';
return EXIT_FAILURE;
}
std::cout << " Kill process with PID = " << pid << '\n';
// Force process termination
kill(pid, SIGTERM);
kill(pid, SIGKILL);
return EXIT_SUCCESS;
}
XCloseDisplay(dpy);
return 0;
}
/*================================================================*\
* I M P L E M E N T A T I O N S *
*================================================================*/
// Get absolute path to file or symbolic link
// Requires: #<unistd.h>, <limits.h>
std::string
read_symlink(std::string const& path)
{
// Create a buffer with size PATH_MAX + 1 filled with 0 ('\0'), null characters
std::string buffer(PATH_MAX, 0);
// ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
ssize_t nread = ::readlink(path.c_str(), &buffer[0], PATH_MAX);
if(nread == -1){ throw std::runtime_error("Error: unable to read symbolic link"); }
buffer.resize(nread);
return buffer;
}
int
x11_get_property_int32(const char* property_name, Display* dpy, Window root_window)
{
Atom p = XInternAtom(dpy, property_name, false);
Atom actual_type;
int format;
unsigned long num_items, bytes_after;
unsigned char* data = nullptr;
int status = XGetWindowProperty( dpy, root_window, p, 0L, 1024L
, false, XA_CARDINAL, &actual_type
, &format, &num_items, &bytes_after
, &data
);
if( status != 0 || num_items < 1)
{
//throw std::runtime_error("Error: failed to get property");
return -1;
}
int value = data[0] | (data[1] << 8)
| (data[2] << 16) | (data[3] << 24);
XFree(data);
return value;
}
std::string
x11_get_property_str(const char* property_name, Display* dpy, Window root_window)
{
Atom p = XInternAtom(dpy, property_name, false);
Atom actual_type;
int format;
unsigned long num_items, bytes_after;
unsigned char* data = nullptr;
int status = XGetWindowProperty( dpy, root_window, p, 0L, 1024L
, false, XA_STRING, &actual_type
, &format, &num_items, &bytes_after
, &data
);
if( status != 0 || num_items < 1)
{
//throw std::runtime_error("Error: failed to get property");
return "<NONE>";
}
std::string value(data, data + num_items);
XFree(data);
return value;
}
template<typename Window_Consumer>
void x11_iterate_child_windows( Display* dpy
,Window root_window
,Window_Consumer&& consumer)
{
// ----- Return parameters of function XQueryTree() -------//
// Note: the memory is allocated and owned by the calling code.
// function.
// Note: '{}' curly brackets gurantees default initialization as 'nullptr'
Window root_return{};
Window parent_return{};
// List of childre window to be returned
Window* children_return{};
// Number of children windows
unsigned int nchildren_return{};
int result = XQueryTree(
dpy // Display
, root_window // Root window
, &root_return // Root window return
, &parent_return // Return paramameter for pointer to parent window
, &children_return // List of children to be returned
, &nchildren_return // Number of children elements in (children_return )
) ;
assert( result != 0 && "Failed to query windows");
std::cout << " [INFO] Number of children windows = " << nchildren_return << '\n';
for(auto n = 0; n < nchildren_return; n++ )
{
consumer(dpy, children_return[n], n);
}
// Dispose, release memory allocated for children windows return
XFree(children_return);
}
// Get Window name
std::string
x11_window_name(Display* dpy, Window wnd)
{
char* window_name;
if( !XFetchName(dpy, wnd, &window_name) )
{ return ""; }
auto output = std::string{window_name};
XFree(window_name);
return output;
}
// Note: It only works for X-client applications running on local machine.
int x11_window_get_pid(Display* dpy, Window wnd)
{
return x11_get_property_int32("_NET_WM_PID", dpy, wnd);
}
bool x11_show_window_attributes(Display* dpy, Window wnd)
{
XWindowAttributes attr;
if( !XGetWindowAttributes(dpy, wnd, &attr) )
{
std::fprintf(stderr, "Error, failed to get window (wnd = %zu) attribute \n"
, wnd);
//exit(EXIT_FAILURE);
return false;
}
auto window_name = x11_window_name(dpy, wnd);
if(window_name == ""){ return false; }
auto is_visible = attr.map_state == IsViewable;
std::cout << "\n\n [INFO] "
<< " ; window_id = " << wnd
<< " ; Visible = " << is_visible
<< " ; Width = " << attr.width
<< " ; Height = " << attr.height
<< '\n';
std::cout << " => Window Name = " << window_name << '\n';
return true;
}
void x11_show_window_properties(Display* dpy, Window wnd)
{
int pid = x11_get_property_int32("_NET_WM_PID", dpy, wnd);
std::cout << " => Window PID (Process ID) = "
<< pid
<< '\n';
try
{
auto path_to_executable_procfs = std::string("/proc/") + std::to_string(pid) + "/exe";
std::cout << " => Window program (executable) = "
<< read_symlink(path_to_executable_procfs) << '\n';
} catch(const std::exception& ex)
{
std::cerr << " [ERROR] " << ex.what() << '\n';
}
std::cout << " => WM_NAME = "
<< x11_get_property_str("WM_NAME", dpy, wnd)
<< '\n';
std::cout << " => WM_CLASS = "
<< x11_get_property_str("WM_CLASS", dpy, wnd)
<< '\n';
std::cout << " => WM_COMMAND = "
<< x11_get_property_str("WM_COMMAND", dpy, wnd)
<< '\n';
// Hostname of machine where the window application is located.
// A window may belong to an application (X-client) running in a
// remote machine.
std::cout << " => WM_CLIENT_MACHINE = "
<< x11_get_property_str("WM_CLIENT_MACHINE", dpy, wnd)
<< '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment