如何从XftFont获取XFontStruct?

问题描述 投票:0回答:1

我正在编写一个简单的X11时钟应用程序,它可以通过调整窗口大小来缩放,

我想在钟面周围画一些数字(1..12),并且数字也应该缩放。

所以我不能使用XLoadQueryFont,它只接受离散的预定义字体大小,无法缩放。

我尝试了 Xft,但未能执行 XftDrawString,这引发了错误 BadPicture

当混合Xlib(如XSetForeground)和Xft(颜色作为参数)的绘图代码时,看起来很混乱。

所以我想也许我可以从XftFont得到XFontStruct?我只需要使用 Xft 来找到与任意字体大小匹配的合适字体。

xft(3)表示,XftFont包含指向XFontStruct的指针:

XftFont 包含通用字体规格和指向核心 XFontStruct 数据或保存 FreeType 和 X Render Extension 数据的结构的指针。

在头文件(xft.h)中,我没有找到指针:

    typedef struct _XftFont {
        int     ascent;
        int     descent;
        int     height;
        int     max_advance_width;
        FcCharSet   *charset;
        FcPattern   *pattern;
    } XftFont;

时钟应用程序:

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xft/Xft.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>

#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_DECOR_ALL (1L << 0) /* [v] */

typedef struct
{
    unsigned long flags;
    unsigned long functions;
    unsigned long decorations;
    long input_mode;
    unsigned long status;
} MotifWmHints;

static MotifWmHints *
get_motif_wm_hints(Display *display,
                   Window window)
{
    Atom property;
    int result;
    Atom actual_type;
    int actual_format;
    unsigned long nitems;
    unsigned long bytes_after;
    unsigned char *data;

    property = XInternAtom(display, "_MOTIF_WM_HINTS", False);
    result = XGetWindowProperty(display, window, property,
                                0, LONG_MAX, False, AnyPropertyType,
                                &actual_type, &actual_format,
                                &nitems, &bytes_after, &data);

    if (result == Success && data != NULL)
    {
        size_t data_size;
        size_t max_size;
        MotifWmHints *hints;

        data_size = nitems * sizeof(long);
        max_size = sizeof(*hints);

        hints = calloc(1, max_size);

        memcpy(hints, data, data_size > max_size ? max_size : data_size);
        XFree(data);

        return hints;
    }

    return NULL;
}

void removeBorder(Display *display,
                  Window window)
{
    MotifWmHints *hints;
    Atom property;
    int nelements;

    hints = get_motif_wm_hints(display, window);
    if (hints == NULL)
    {
        hints = calloc(1, sizeof(*hints));
        hints->decorations = 0;
    }

    hints->flags |= MWM_HINTS_DECORATIONS;
    hints->decorations = 0;
    property = XInternAtom(display, "_MOTIF_WM_HINTS", False);
    nelements = sizeof(*hints) / sizeof(long);

    XChangeProperty(display, window, property, property, 32, PropModeReplace,
                    (unsigned char *)hints, nelements);

    free(hints);
}

int clock_margin_h = 80;
int clock_margin_v = 80;
int clock_radius = 100;

Colormap colormap;
XColor blue_color;
XColor red_color;
XftDraw *draw = NULL;
XftFont *font = NULL;

void drawClock(Display *display, Window window, int hour, int minute, int second, int usec)
{
    int screen = DefaultScreen(display);

    int clock_center_x = clock_radius + clock_margin_h;
    int clock_center_y = clock_radius + clock_margin_v;

    float hour_len = 0.4;
    float minute_len = 0.6;
    float second_len = 0.8;

    XGCValues values;
    GC gc = XCreateGC(display, window, 0, &values);

    // Draw clock face
    XSetForeground(display, gc, blue_color.pixel);
    XFillArc(display, window, gc, clock_center_x - clock_radius, clock_center_y - clock_radius,
             2 * clock_radius, 2 * clock_radius, 0, 360 * 64);

    double hour_angle = (-(hour + minute / 60.0) / 12 + 1 / 4.0) * M_PI * 2;
    double minute_angle = (-minute / 60.0 + 1 / 4.0) * M_PI * 2;
    double second_angle = (-(second + usec / 1000000.0) / 60.0 + 1 / 4.0) * M_PI * 2;

    // Draw hour hand (white color)
    XSetForeground(display, gc, WhitePixel(display, screen));
    XDrawLine(display, window, gc, clock_center_x, clock_center_y,
              clock_center_x + hour_len * clock_radius * cos(hour_angle),
              clock_center_y - hour_len * clock_radius * sin(hour_angle));

    // Draw minute hand (white color)
    XDrawLine(display, window, gc, clock_center_x, clock_center_y,
              clock_center_x + minute_len * clock_radius * cos(minute_angle),
              clock_center_y - minute_len * clock_radius * sin(minute_angle));

    // Draw second hand (red color)
    XSetForeground(display, gc, red_color.pixel);
    XDrawLine(display, window, gc, clock_center_x, clock_center_y,
              clock_center_x + second_len * clock_radius * cos(second_angle),
              clock_center_y - second_len * clock_radius * sin(second_angle));

    // Draw numbers 1-12 around the clock face
    if (font)
    {
        // XSetFont(display, gc, font_info->fid);
        // XSetForeground(display, gc, WhitePixel(display, screen));
        XftDraw *draw = XftDrawCreate(display, window,
                                      DefaultVisual(display, screen),
                                      DefaultColormap(display, screen));

        XftColor color;
        color.color.red = 65535;
        color.color.green = 65535;
        color.color.blue = 65535;
        color.color.alpha = 65535;

        for (int i = 1; i <= 12; i++)
        {
            double angle = (-(i) / 12.0 + 1 / 4.0) * M_PI * 2;
            int x = clock_center_x + 1 * clock_radius * cos(angle) - 5;
            int y = clock_center_y - 1 * clock_radius * sin(angle) + 10;
            char number[10];
            sprintf(number, "%d", i);
            // XDrawString(display, window, gc, x, y, number, strlen(number));
            XftDrawStringUtf8(draw, &color, font, x, y, number, strlen(number));
        }
        XftDrawDestroy(draw);
    }
    else
    {
        fprintf(stderr, "Error: Unable to load font\n");
    }

    XFreeGC(display, gc);
}

void updateClock(Display *display, Window window)
{
    time_t now = time(NULL);
    struct tm *local_time = localtime(&now);

    struct timeval tvTime;
    gettimeofday(&tvTime, NULL);

    int hour = local_time->tm_hour % 12;
    int minute = local_time->tm_min;
    int second = local_time->tm_sec;

    drawClock(display, window, hour, minute, second, tvTime.tv_usec);
}

int error_logger(
    Display *display,
    XErrorEvent *event)
{
    char msg[200];
    XGetErrorText(display, event->error_code, msg, sizeof(msg));

    fprintf(stderr, "[serial %d] [type %d] error %d (%s): request %d.%d\n", //
            event->serial,
            event->type,
            event->error_code,
            msg,
            event->request_code,
            event->minor_code);
}

static int wait_fd(int fd, long usec)
{
    struct timeval tv;
    fd_set in_fds;
    FD_ZERO(&in_fds);
    FD_SET(fd, &in_fds);
    tv.tv_sec = usec / 1000000;
    tv.tv_usec = usec % 1000000;
    return select(fd + 1, &in_fds, 0, 0, &tv);
}

int XNextEventTimeout(Display *display, XEvent *event, double seconds)
{
    if (XPending(display) || wait_fd(ConnectionNumber(display), seconds))
    {
        XNextEvent(display, event);
        return 0;
    }
    else
    {
        // event->type = 0;
        return 1;
    }
}

int main()
{
    XSetErrorHandler(error_logger);

    Display *display = XOpenDisplay(NULL);
    if (display == NULL)
    {
        fprintf(stderr, "Error: Could not open display\n");
        return 1;
    }

    Window root = DefaultRootWindow(display);
    int screen = DefaultScreen(display);

    Window origFocusWindow;
    int origRevert;
    XGetInputFocus(display, &origFocusWindow, &origRevert);

    // Create a window with transparent background
    XVisualInfo vinfo;
    XMatchVisualInfo(display, screen, 32, TrueColor, &vinfo);
    XSetWindowAttributes attr;
    colormap = XCreateColormap(display, root, vinfo.visual, AllocNone);
    attr.colormap = colormap;
    attr.border_pixel = 0;
    attr.background_pixel = 0;
    // attr.override_redirect = False;

    int left = 100, top = 100;
    int width = (clock_radius + clock_margin_h) * 2;
    int height = (clock_radius + clock_margin_v) * 2;
    int border_width = 0;
    Window window = XCreateWindow(display, root, left, top, width, height, border_width,
                                  vinfo.depth, InputOutput, vinfo.visual,
                                  CWColormap | CWBorderPixel | CWBackPixel, &attr);
    // removeBorder(display, window);

    Colormap colormap = DefaultColormap(display, screen);
    XAllocNamedColor(display, colormap, "blue", &blue_color, &blue_color);
    XAllocNamedColor(display, colormap, "red", &red_color, &red_color);

    font = XftFontOpenName(display, screen, "Sans:size=10");

    // Set window background to transparent
    // XSetWindowBackground(display, window, 0);
    XClearWindow(display, window);

    XMapWindow(display, window);
    XSelectInput(display, window, ExposureMask | StructureNotifyMask);

    // XMapWindow(display, focusWindow);
    // XRaiseWindow(display, focusWindow);
    // XSelectInput(display, focusWindow, ExposureMask | KeyPressMask);
    // XSync(display, True);
    // int r = XSetInputFocus(display, focusWindow, RevertToParent, CurrentTime);

    while (1)
    {
        XEvent event;
        int timeout = XNextEventTimeout(display, &event, 100 * 1000);
        if (timeout)
        {
            updateClock(display, window);
            continue;
        }

        switch (event.type)
        {
        case ConfigureNotify:
            XConfigureEvent xce = event.xconfigure;
            if (xce.width < xce.height)
                clock_radius = xce.width / 2 - clock_margin_h;
            else
                clock_radius = xce.height / 2 - clock_margin_v;

            int font_height = clock_radius / 12;

            if (font)
            {
                XftFontClose(display, font);
                font = NULL;
            }

            char font_name[80];
            sprintf(font_name, "Sans:size=%d", font_height);
            font = XftFontOpenName(display, screen, font_name);

            updateClock(display, window);
            break;

        case Expose:
            updateClock(display, window);

            Window focusWindow;
            int revert;
            XGetInputFocus(display, &focusWindow, &revert);
            if (focusWindow == window)
            {
                XSetInputFocus(display, origFocusWindow, RevertToParent, CurrentTime);
            }
            break;

        case MapNotify:
        case ClientMessage:
            break;

        default:
            printf("other event: %d\n", event.type);
        }
    }

    XCloseDisplay(display);

    return 0;
}
x11 freetype2 xft
1个回答
0
投票

如何从XftFont获取XFontStruct?

你不知道。你不能,也不需要。 Xft 是一个客户端大小的字体渲染系统。核心 X 字体是服务器大小的,它们不知道你有什么客户端。

我尝试了 Xft,但未能执行 XftDrawString,这引发了错误 BadPicture。

该错误是由于视觉不匹配造成的。您在某些地方使用默认视觉效果,而在其他地方使用找到并匹配的视觉效果。对同一窗口使用相同的视觉效果。作为一个简单快速但肮脏的修复,请将

vinfo
设为全局并始终使用
vinfo.visual

xft(3)表示,XftFont包含一个指向XFontStruct的指针:

您的手册页版本已过时。不仅头文件没有引用 XFontStruct,整个 libXft 源也没有提及它。 libXft 的一个非常旧的版本(包含在 XOrg X11R6.7 版本中)在手册页中提到了 XFontStruct,但在源代码中没有提到。 X11R6.9 版本中不再提及。我在互联网上找不到旧版本的 libXft(其作者也找不到)。

所以我想也许我可以从 XftFont 获取 XFontStruct?

不。不要尝试那样做。使用客户端字体渲染。它有效。

© www.soinside.com 2019 - 2024. All rights reserved.