使用C Gtk写一个360网速指示器

原文:https://www.jianshu.com/p/169682d4d71a

效果如下:
使用C Gtk写一个360网速指示器_第1张图片

##1. 实现悬浮窗
实现悬浮窗的是使用GTK的GTK_WINDOW_POPUP类型,它可以实现没有任务窗口栏显示。

window = gtk_window_new(GTK_WINDOW_POPUP);

##2. 获取网络、内存信息
主要是读取/proc/net/dev/proc/meminfo

int getMemPercentage() {

    FILE *fd_men = fopen("/proc/meminfo", "r");
    if(fd_men == NULL) {
        perror("open file /proc/meminfo error");
        exit(-1);
    }

    char tmp[255];
    fgets(tmp, 255, fd_men);
    unsigned long men = 0;
    unsigned long free = 0;
    sscanf(tmp, "MemTotal: %ld KB", &men);
    fgets(tmp, 255, fd_men);
    fgets(tmp, 255, fd_men);
    sscanf(tmp, "MemAvailable: %ld KB", &free);

    unsigned long use = men - free;
    int percent = ((float)use / (float)men) * 100;


    fclose(fd_men);
    return percent;
}

// 获取网速
void getNetworkBandWidth(unsigned long long int *receiveBytes, unsigned long long int *sendBytes) {
    char *buf;
    const int bufSize = 255;
    FILE *devfd;

    buf = (char *) calloc(bufSize, 1);

    devfd = fopen("/proc/net/dev", "r");
    if (devfd == NULL) {
        perror("open file /proc/net/dev failure.");
        exit(-1);
    }

    // Ignore the first and second lines of the file.
    fgets(buf, bufSize, devfd); // fgets will return if reading a newline.
    fgets(buf, bufSize, devfd);
    *receiveBytes = 0;
    *sendBytes = 0;

    while (fgets(buf, bufSize, devfd)) {
        unsigned long long int rBytes, sBytes;
        char *line = strdup(buf);

        char *dev;
        dev = strtok(line, ":");
        gchar *is_lo = g_strrstr(dev, "lo");
        if (is_lo != NULL) { // if end with lo
            continue;
        }
        sscanf(buf + strlen(dev) + 2, "%llu %*d %*d %*d %*d %*d %*d %*d %llu", &rBytes, &sBytes);
        *receiveBytes += rBytes;
        *sendBytes += sBytes;
        free(line);
    }

    fclose(devfd);
    free(buf);
}

网速的逻辑参照deepin的任务管理器的网速获取。

3. 设置透明

需要桌面系统支持。

// 配置透明
static void tran_setup(GtkWidget *win)
{
    GdkScreen *screen;
    GdkVisual *visual;

    gtk_widget_set_app_paintable(win, TRUE);
    screen = gdk_screen_get_default();
    visual = gdk_screen_get_rgba_visual(screen);

    if (visual != NULL && gdk_screen_is_composited(screen))
    {
        gtk_widget_set_visual(win, visual); // set transparent
        g_print("is_composited=true");
    }
    else
    { // not support
        g_print("Your system not suppot transparent window!\nPlease check if you have turned off this feature.\n");
    }
}

4. 绘制界面

绘制界面是使用的是cairo

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
                              gpointer user_data)
{

    cairo_t *first_cr, *second_cr;
    cairo_surface_t *first, *second;

    int width, height;
    char tmp[255];

    GtkWidget *win = gtk_widget_get_toplevel(widget);

    gtk_window_get_size(GTK_WINDOW(win), &width, &height);

    first = cairo_surface_create_similar(cairo_get_target(cr),
                                         CAIRO_CONTENT_COLOR_ALPHA, width, height);

    second = cairo_surface_create_similar(cairo_get_target(cr),
                                          CAIRO_CONTENT_COLOR_ALPHA, width, height);

    first_cr = cairo_create(first);
    second_cr = cairo_create(second);

    // draw left circle
    cairo_translate(first_cr, SIZE / 2 + PEN_WIDTH, height / 2); // setup center point

    cairo_set_line_width(first_cr, PEN_WIDTH);                                            // setup pen width
    cairo_set_source_rgba(first_cr, get_color(193), get_color(205), get_color(193), 0.5); // setup color

    cairo_arc(first_cr, 0, 0, SIZE / 2, 0, 2 * M_PI);
    cairo_stroke_preserve(first_cr); // draw stroke

    cairo_set_line_width(first_cr, PEN_WIDTH);     // setup pen width
    cairo_set_source_rgba(first_cr, 1, 1, 1, 0.3); // free memery color
    cairo_fill(first_cr);                          // fill free mem

    // calc the arc start angle and end angle
    float start = 0.5 - ((float)memPercentage / 100);
    float end = 0.5 + ((float)memPercentage / 100);

    // fill use memory
    cairo_arc(first_cr, 0, 0, SIZE / 2 - 2, M_PI * start, M_PI * end);
    if (memPercentage <= 50)
    {
        cairo_set_source_rgb(first_cr, 0.1, 0.7, 0.1); // green
    }
    else if (memPercentage <= 79)
    {
        cairo_set_source_rgb(first_cr, 1, 0.6, 0); // yellow
    }
    else
    {
        cairo_set_source_rgb(first_cr, 0.9, 0, 0); // red
    }

    cairo_fill(first_cr); // fill use memory

    // draw memory info
    cairo_set_source_rgb(first_cr, 0.1, 0.1, 0.1);
    cairo_select_font_face(first_cr, MEM_FONT,
                           CAIRO_FONT_SLANT_NORMAL,
                           CAIRO_FONT_WEIGHT_BOLD);
    cairo_set_font_size(first_cr, 16);
    cairo_move_to(first_cr, -12, 4);
    sprintf(tmp, "%d%%", memPercentage);
    cairo_show_text(first_cr, tmp);

    if (SHOW_NETWORK_SPEED)
    {
        // draw an incomplete rectangle, missing a semicircle on the left
        cairo_set_source_rgba(second_cr, get_color(193), get_color(205), get_color(193), 0.9);
        cairo_arc(second_cr, 0, SIZE / 2, SIZE / 2, -0.5 * M_PI, 0.5 * M_PI);                             // draw semicircle, like ")"
        cairo_line_to(second_cr, RIGHT_CIRCLE_WIDTH, RIGHT_CIRCLE_SIZE + (SIZE - RIGHT_CIRCLE_SIZE) / 2); // lint to rectangle (right, bottom)
        cairo_line_to(second_cr, RIGHT_CIRCLE_WIDTH, (SIZE - RIGHT_CIRCLE_SIZE) / 2);                     // line to (right, top)
        cairo_fill(second_cr);

        cairo_close_path(second_cr);

        // draw right arc
        cairo_set_source_rgba(second_cr, get_color(193), get_color(205), get_color(193), 0.9); // The same color as the rectangle
        cairo_translate(second_cr, RIGHT_CIRCLE_WIDTH, SIZE / 2);                              // set up (0, 0) to (right_edge,center)
        cairo_arc(second_cr, 0, 0, RIGHT_CIRCLE_SIZE / 2, -0.5 * M_PI, 0.5 * M_PI);
        cairo_fill(second_cr);

        // draw net-spped text
        cairo_set_source_rgb(second_cr, 0.1, 0.1, 0.1);
        cairo_select_font_face(second_cr, NET_FONT,
                               CAIRO_FONT_SLANT_NORMAL,
                               CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size(second_cr, 12);

        cairo_move_to(second_cr, -((RIGHT_CIRCLE_WIDTH - SIZE / 2) + NET_SPEED_TEXT_MARGIN_LEFT), -8);
        if (csD >= 2048)
        {
            sprintf(tmp, " ↑ %.2f m/s", kb2m(csD));
        }
        else
        {
            sprintf(tmp, " ↑ %lu kb/s", csD);
        }

        cairo_show_text(second_cr, tmp);
        cairo_move_to(second_cr, -((RIGHT_CIRCLE_WIDTH - SIZE / 2) + NET_SPEED_TEXT_MARGIN_LEFT), 15);
        if (crD >= 1024)
        {
            sprintf(tmp, " ↓ %.2f m/s", kb2m(crD));
        }
        else
        {
            sprintf(tmp, " ↓ %lu kb/s", crD);
        }

        cairo_show_text(second_cr, tmp);
    }

    // add to cr
    cairo_set_operator(first_cr, CAIRO_OPERATOR_DEST_OVER);     // let the left circle cover the right rectangle
    cairo_set_source_surface(first_cr, second, 0, -(SIZE) / 2); // set up (0, 0) to right circle's top position
    cairo_paint(first_cr);

    cairo_set_source_surface(cr, first, 0, 0);
    cairo_paint(cr);

    // destroy
    cairo_surface_destroy(first);
    cairo_surface_destroy(second);
    cairo_destroy(first_cr);
    cairo_destroy(second_cr);

    return FALSE;
}
}

比较麻烦的是内存占用的绘制,这里是使用的绘制圆弧实现的,圆的中心点是(0, 0), 下图是角度示意:
使用C Gtk写一个360网速指示器_第2张图片
要绘制内存填充区域,需要知道内存占用比例,根据这个比例,计算圆弧的开始角度和结束角度。
假设内存占用50%,那么应该是 开始角度为:0 * M_PI,结束角度为 1 * M_PI, 等价于以下代码动态实现:

float start = 0.5 - ((float)memPercentage / 100);
float end = 0.5 + ((float)memPercentage / 100);

start为负数的时候,自然会逆时针绘制。

再者,绘制界面的时候,右边绘制了一个少了个半圆的矩形+一个半圆,主要使用线来画。
使用C Gtk写一个360网速指示器_第3张图片

一开始,使用组合方式,绘制的是正方形,发现左边的圆设置透明之后,不管怎样设置组合方式都会现实出右边的正方形。

自启动

把二进制文件放到$PATH里面,加可执行权限。
在自启动管理软件里面执行这个二进制文件,如果发现会边框黑,加上sleep 20。等你的桌面完全启动好再启动。

GitHub

https://github.com/Baloneo/network-ball-gtk/

你可能感兴趣的:(Ubuntu,linyx,gtk)