根窗口
最后修改于 2023 年 7 月 17 日
在 Cairo 图形教程的这一部分,我们将处理根窗口。根窗口是我们通常放置图标快捷方式的桌面窗口。
操作根窗口是可能的。从程序员的角度来看,它只是一个特殊的窗口。
透明窗口
我们的第一个例子将创建一个透明窗口。我们将看到窗口对象下方的内容。
#include <cairo.h> #include <gtk/gtk.h> static void do_drawing(cairo_t *); 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); } } static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { do_drawing(cr); return FALSE; } static void do_drawing(cairo_t *cr) { cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); tran_setup(window); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 300, 250); gtk_window_set_title(GTK_WINDOW(window), "Transparent window"); gtk_widget_show_all(window); gtk_main(); return 0; }
要创建透明窗口,我们获取屏幕对象的 visual,并将其设置给我们的窗口。在 on_draw
方法中,我们在屏幕的 visual 对象上绘制。这会产生一种部分透明的错觉。
gtk_widget_set_app_paintable(win, TRUE);
我们必须设置应用程序需要被绘制。
screen = gdk_screen_get_default();
gdk_screen_get_default
方法返回屏幕对象。
visual = gdk_screen_get_rgba_visual(screen);
从屏幕窗口,我们获取它的 visual。visual 包含低级显示信息。
if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); }
并非所有显示都支持此操作。因此,我们检查我们的屏幕是否支持合成,并且返回的 visual 不为 None。我们将屏幕的 visual 设置为我们窗口的 visual。
static void do_drawing(cairo_t *cr) { cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_paint(cr); }
我们使用一个部分透明的源来绘制在屏幕窗口上。CAIRO_OPERATOR_SOURCE
创建了一个操作,我们将绘制内容覆盖在源(即屏幕窗口)之上。要实现完全透明,我们将 alpha 值设置为 0 或使用 CAIRO_OPERATOR_CLEAR
操作符。

截屏
根窗口在截屏时也很重要。
#include <cairo.h> #include <gdk/gdk.h> int main (int argc, char *argv[]) { gdk_init(&argc, &argv); GdkWindow *root_win = gdk_get_default_root_window(); gint width = gdk_window_get_width(root_win); gint height = gdk_window_get_height(root_win); cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height); cairo_t *cr = cairo_create(surface); gdk_cairo_set_source_pixbuf(cr, pb, 0, 0); cairo_paint(cr); cairo_surface_write_to_png(surface, "image.png"); cairo_destroy(cr); cairo_surface_destroy(surface); return 0; }
本例捕获整个屏幕的快照。在此示例中,我们不使用完整的 GTK 窗口系统。我们使用 Cairo 和 GDK 库来完成这项工作。
gdk_init(&argc, &argv);
gdk_init
初始化 GDK 库并连接到窗口系统。
GdkWindow *root_win = gdk_get_default_root_window();
我们使用 gdk_get_default_root_window
函数调用来获取根窗口。
gint width = gdk_window_get_width(root_win); gint height = gdk_window_get_height(root_win);
我们确定根窗口的宽度和高度。
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
创建一个空的图像表面。它的尺寸与根窗口相同。
GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);
我们使用 gdk_pixbuf_get_from_window
函数调用从根窗口获取一个 pixbuf。Pixbuf 是一个描述内存中图像的对象。
cairo_t *cr = cairo_create(surface); gdk_cairo_set_source_pixbuf(cr, pb, 0, 0); cairo_paint(cr);
在上面的代码行中,我们在之前创建的图像表面上创建了一个 Cairo 绘图上下文。我们将 pixbuf 放在绘图上下文中,并将其绘制到表面上。
cairo_surface_write_to_png(surface, "image.png");
图像表面使用 write_to_png
方法写入 PNG 图像。
cairo_destroy(cr); cairo_surface_destroy(surface);
我们清理资源。
显示消息
在第三个例子中,我们在桌面窗口上显示一条消息。
#include <cairo.h> #include <gtk/gtk.h> #include <pango/pango.h> static void do_drawing(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { do_drawing(cr); return FALSE; } static void do_drawing(cairo_t *cr) { cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); } static void setup(GtkWidget *win) { gtk_widget_set_app_paintable(win, TRUE); gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK); gtk_window_set_keep_below(GTK_WINDOW(win), TRUE); GdkScreen *screen = gdk_screen_get_default(); GdkVisual *visual = gdk_screen_get_rgba_visual(screen); if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); } } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *lbl; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); setup(window); lbl = gtk_label_new("ZetCode, tutorials for programmers"); PangoFontDescription *fd = pango_font_description_from_string("Serif 20"); gtk_widget_modify_font(lbl, fd); gtk_container_add(GTK_CONTAINER(window), lbl); GdkColor color; gdk_color_parse("white", &color); gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 350, 250); gtk_widget_show_all(window); gtk_main(); return 0; }
代码在根窗口上显示一个消息标签。
static void do_drawing(cairo_t *cr) { cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_OVER); }
我们使用 CAIRO_OPERATOR_CLEAR
操作符来清除窗口背景。然后我们设置 CAIRO_OPERATOR_OVER
以便绘制标签小部件。
gtk_widget_set_app_paintable(win, TRUE);
我们将操作应用程序窗口,所以我们使其可绘制。
gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
实现此窗口提示会移除窗口边框和装饰。
gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);
我们始终将应用程序保持在底部,正好在根窗口之上。
GdkScreen *screen = gdk_screen_get_default(); GdkVisual *visual = gdk_screen_get_rgba_visual(screen); if (visual != NULL && gdk_screen_is_composited(screen)) { gtk_widget_set_visual(win, visual); }
我们将屏幕的 visual 设置为我们应用程序的 visual。
lbl = gtk_label_new("ZetCode, tutorials for programmers");
我们创建一个消息标签。
PangoFontDescription *fd = pango_font_description_from_string("Serif 20"); gtk_widget_modify_font(lbl, fd);
借助 Pango 模块,我们为文本选择特定的字体。
gtk_container_add(GTK_CONTAINER(window), lbl);
标签被放置到窗口中。
GdkColor color; gdk_color_parse("white", &color); gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);
我们将文本修改为白色。

在本章中,我们处理了 Cairo 中的桌面窗口。