原文:https://www.jianshu.com/p/169682d4d71a
##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的任务管理器的网速获取。
需要桌面系统支持。
// 配置透明
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");
}
}
绘制界面是使用的是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), 下图是角度示意:
要绘制内存填充区域,需要知道内存占用比例,根据这个比例,计算圆弧的开始角度和结束角度。
假设内存占用50%,那么应该是 开始角度为:0 * M_PI
,结束角度为 1 * M_PI
, 等价于以下代码动态实现:
float start = 0.5 - ((float)memPercentage / 100);
float end = 0.5 + ((float)memPercentage / 100);
start
为负数的时候,自然会逆时针绘制。
再者,绘制界面的时候,右边绘制了一个少了个半圆的矩形+一个半圆,主要使用线来画。
一开始,使用组合方式,绘制的是正方形,发现左边的圆设置透明之后,不管怎样设置组合方式都会现实出右边的正方形。
把二进制文件放到$PATH
里面,加可执行权限。
在自启动管理软件里面执行这个二进制文件,如果发现会边框黑,加上sleep 20
。等你的桌面完全启动好再启动。
https://github.com/Baloneo/network-ball-gtk/