Created
October 26, 2020 15:13
-
-
Save caiorss/639df76864d014ead12936fbd361be73 to your computer and use it in GitHub Desktop.
Xlib X11 query windows information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} ) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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