X窗口管理器调用窗口图标获取脚本返回None问题求助
问题
开发X窗口管理器过程中遇到一个问题:单独运行窗口图标获取脚本时,传入窗口ID能正常返回Pillow Image对象;但在窗口管理器内部调用该脚本,传入已确认有效的窗口ID,却返回None。
Python 图标获取脚本
import ctypes import os from PIL import Image import io class IconData(ctypes.Structure): _fields_ = [ ("width", ctypes.c_ulong), ("height", ctypes.c_ulong), ("pixels", ctypes.POINTER(ctypes.c_ulong)), ("num_icons", ctypes.c_int), ] lib_paths = [ os.path.join(os.path.dirname(__file__), "libgeticon.so"), # Linux os.path.join(os.path.dirname(__file__), "libgeticon.dylib"), # macOS (ewwww) os.path.join(os.path.dirname(__file__), "geticon.dll"), # Windows (EWWWWW) ] lib = None for path in lib_paths: if os.path.exists(path): try: lib = ctypes.CDLL(path) break except OSError as e: print(f"Warning: Could not load {path}: {e}") lib = None if lib is None: raise RuntimeError("Could not find or load libgeticon shared library. " "Please ensure it's compiled and in the same directory as xicon.py " "or in a system-wide library path.") lib.get_window_icon.argtypes = [ctypes.c_void_p, ctypes.c_ulong] lib.get_window_icon.restype = ctypes.POINTER(IconData) lib.free_icon_data.argtypes = [ctypes.POINTER(IconData)] lib.free_icon_data.restype = None def get_window_icon(window_id: int) -> Image.Image | None: """ Retrieves the icon for a given X Window ID. Args: window_id: The ID of the X Window (integer). Returns: A PIL.Image.Image object if an icon is found, otherwise None. The image will be in RGBA format. """ dpy = ctypes.cdll.LoadLibrary('libX11.so.6').XOpenDisplay(None) # Linux if not dpy: print("Error: Could not open X display.") return None icon_data_ptr = lib.get_window_icon(dpy, ctypes.c_ulong(window_id)) if icon_data_ptr and icon_data_ptr.contents.pixels: icon_data = icon_data_ptr.contents width = icon_data.width height = icon_data.height pixels_ptr = icon_data.pixels raw_pixels = [pixels_ptr[i] for i in range(width * height)] rgba_pixels = [] for pixel_val in raw_pixels: a = (pixel_val >> 24) & 0xFF r = (pixel_val >> 16) & 0xFF g = (pixel_val >> 8) & 0xFF b = pixel_val & 0xFF rgba_pixels.extend([r, g, b, a]) img = Image.frombytes("RGBA", (width, height), bytes(rgba_pixels)) lib.free_icon_data(icon_data_ptr) ctypes.cdll.LoadLibrary('libX11.so.6').XCloseDisplay(dpy) return img else: ctypes.cdll.LoadLibrary('libX11.so.6').XCloseDisplay(dpy) print(f"No icon found for window ID: {window_id}") return None if __name__ == '__main__': try: test_window_id = input("Enter a window ID: ") icon_image = get_window_icon(test_window_id) if icon_image: print(f"Icon is {icon_image.width}x{icon_image.height} pixels") icon_image.save("window_icon.png") else: print("Failed to retrieve icon") except Exception as e: print(e)
C 语言库代码(geticon.c)
// geticon.c #include "geticon.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <X11/Xatom.h> // <--- ADDED THIS LINE // Helper function to ignore X errors int (*old_error_handler)(Display *dpy, XErrorEvent *ev); static int ignore_x_errors(Display *dpy, XErrorEvent *ev) { // Optionally print error, but for library, might want to be quiet. // fprintf(stderr, "X Error: serial %lu, error_code %d, request_code %d, minor_code %d\n", // ev->serial, ev->error_code, ev->request_code, ev->minor_code); return 0; } IconData* get_window_icon(Display *dpy, Window win) { Atom actual_type; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char *prop = NULL; IconData *icon_data = NULL; // Set error handler to avoid Xlib exiting on BadWindow old_error_handler = XSetErrorHandler(ignore_x_errors); Atom net_wm_icon_atom = XInternAtom(dpy, "_NET_WM_ICON", False); if (net_wm_icon_atom == None) { fprintf(stderr, "Error: _NET_WM_ICON atom not found.\n"); goto cleanup; } // Get the _NET_WM_ICON property // The property data is typically an array of CARDINAL, where each CARDINAL // represents a width, then a height, followed by width*height CARDINALs for the pixels. // This can repeat for multiple icon sizes. int status = XGetWindowProperty(dpy, win, net_wm_icon_atom, 0, // offset ~0L, // length (get all data) False, // delete XA_CARDINAL, // desired type &actual_type, &actual_format, &nitems, &bytes_after, &prop); // Restore old error handler XSetErrorHandler(old_error_handler); if (status != Success || prop == NULL || actual_type != XA_CARDINAL || actual_format != 32) { if (status == BadWindow) { fprintf(stderr, "Error: Window ID 0x%lx does not exist.\n", win); } else if (status != Success) { fprintf(stderr, "Error: XGetWindowProperty failed with status %d.\n", status); } else if (prop == NULL) { fprintf(stderr, "Error: No _NET_WM_ICON property found or property is empty.\n"); } else if (actual_type != XA_CARDINAL) { fprintf(stderr, "Error: _NET_WM_ICON property has wrong type (expected XA_CARDINAL, got %lu).\n", actual_type); } else if (actual_format != 32) { fprintf(stderr, "Error: _NET_WM_ICON property has wrong format (expected 32-bit, got %d-bit).\n", actual_format); } goto cleanup; } if (nitems == 0) { fprintf(stderr, "Warning: _NET_WM_ICON property is empty.\n"); goto cleanup; // No icon data } icon_data = (IconData*) malloc(sizeof(IconData)); if (!icon_data) { perror("Failed to allocate IconData"); goto cleanup; } icon_data->width = 0; icon_data->height = 0; icon_data->pixels = NULL; icon_data->num_icons = 0; unsigned long *data_ptr = (unsigned long *)prop; unsigned long remaining_items = nitems; // The _NET_WM_ICON property can contain multiple icon images. // Each image starts with width, then height, then width*height pixels. // We will extract the first valid icon found. while (remaining_items >= 2) { // Need at least width and height unsigned long current_width = *data_ptr++; unsigned long current_height = *data_ptr++; remaining_items -= 2; if (current_width == 0 || current_height == 0) { fprintf(stderr, "Warning: Encountered icon with zero dimension (%lu x %lu).\n", current_width, current_height); // Skip to the next potential icon if dimensions are invalid data_ptr += (current_width * current_height); // Attempt to skip based on reported size remaining_items -= (current_width * current_height); continue; } unsigned long pixel_count = current_width * current_height; if (remaining_items < pixel_count) { fprintf(stderr, "Error: Incomplete icon data. Expected %lu pixels, but only %lu remaining.\n", pixel_count, remaining_items); // This indicates a malformed property, try to clean up. if (icon_data->pixels) free(icon_data->pixels); free(icon_data); icon_data = NULL; XFree(prop); // Free the X property data return NULL; } // We'll take the first valid icon we find. If the user wants to choose, // we'd return a list of IconData, but the prompt asked for "the icon". // It's common to take the largest or a specific size. For simplicity, we'll take the first. icon_data->width = current_width; icon_data->height = current_height; icon_data->pixels = (unsigned long *) malloc(pixel_count * sizeof(unsigned long)); if (!icon_data->pixels) { perror("Failed to allocate pixels"); if (icon_data) free(icon_data); icon_data = NULL; goto cleanup; } memcpy(icon_data->pixels, data_ptr, pixel_count * sizeof(unsigned long)); icon_data->num_icons = 1; // We extracted one icon // Successfully extracted one icon, we can stop here. break; } cleanup: if (prop) { XFree(prop); // Always free data returned by XGetWindowProperty } return icon_data; } void free_icon_data(IconData *data) { if (data) { if (data->pixels) { free(data->pixels); } free(data); } }
头文件(geticon.h)
// geticon.h #ifndef GET_ICON_H #define GET_ICON_H #include <X11/Xlib.h> #include <X11/Xutil.h> // Structure to hold icon data typedef struct { unsigned long width; unsigned long height; unsigned long *pixels; // ARGB pixel data int num_icons; // Number of icon images in the property } IconData; // Function to get window icon data // Returns an IconData struct. Caller is responsible for freeing pixels. // Returns NULL on error. IconData* get_window_icon(Display *dpy, Window win); // Function to free IconData void free_icon_data(IconData *data); #endif // GET_ICON_H
窗口管理器中的调用片段
def _draw_text_on_window(self, target_window, text, x, y, max_width=None, text_color=None, target_window_for_icon=None): """ Draws text on a given Xlib window. Clears the area before drawing. If text_color is provided, sets the GC's foreground to that color. """ if not target_window or not text: return target_window.clear_area(0, 0, 0, 0, False) # Clear full window for simplicity if text_color is not None: self.gc.change(foreground=text_color) else: # If no text_color is specified, use a default black (monochrome text is required) self.gc.change(foreground=self.screen.black_pixel) text_bytes = text.encode('utf-8') # Simple truncation if text is too long (can be improved with font metrics) # Assuming ~6 pixels per character for a rough estimate if max_width: max_chars = int(max_width / 6) # Rough character width if len(text_bytes) > max_chars: text_bytes = text_bytes[:max_chars - 3] + b".." target_window.draw_text(self.gc, x + 18, y, text_bytes) import utils.xicon as xr print(target_window_for_icon.id) print(target_window_for_icon.get_wm_name()) icon_image = xr.get_window_icon(int(target_window_for_icon.id)) if icon_image: icon_image = icon_image.resize((16, 16), resample=Image.Resampling.NEAREST) gcx = target_window.create_gc(foreground = 0xFFFFFF, background = 0x000000) try: target_window.put_pil_image(gcx, BUTTON_PADDING, BUTTON_PADDING, icon_image) except Exception as e: print(f"Error putting PIL image on window: {e}", file=sys.stderr) else: print("No icon image available to draw.", file=sys.stderr) self.disp.flush()
内容的提问来源于stack exchange,提问作者SpinningCubes
相关产品推荐
相关产品推荐

