GTK/GDK创建顶层透明窗口

写OSD控件时希望能实现这么种操作:平时OSD是镂空显示在屏幕上,但是可以用鼠标移动。一旦鼠标移动到文字的包围矩形之后,显示它的包围矩形作为OSD的背景(也就是说,不镂空了),以提示用户可以移动它。

实现的思路很简单。有三个窗口,第一个是显示OSD的窗口,它是不规则形状的,因此它对鼠标事件的感应也是不规则形状的,这就使得我们要创建第二个窗口,它是一个透明窗口,窗口大小就是OSD的包围矩形,用来接收鼠标事件,一旦接收到了鼠标事件,我们就显示第三个窗口,也就是背景窗口。注意这里的窗口是GdkWindow,而不是GtkWindow。这三个窗口都是顶层窗口,而且是弹出式窗口,在所有工作区都处于最顶端,不会被窗口管理器管理,也就是使用GTK_WINDOW_POPUP创建的那种窗口。一般来说,输入法啊弹出气泡啊都是弹出窗口,OSD也不例外。

创建一个GdkWindow需要两个参数:GdkWindowAttr结构的attr,表示窗口的各项属性;GdkWindowAttributesType枚举的attr_mask,标志创建时考虑attr中的哪些成员的值。

一般来说,创建一个弹出窗口的关键属性是:
 

//设置窗口类型为TEMP,这样创建的就是弹出窗口了(GTK_WINDOW_POPUP类型的窗口和菜单项都是这种类型窗口)
attr.window_type = GDK_WINDOW_TEMP;
//设置窗口类为输入输出,这样才会在屏幕上显示出来
attr.wclass = GDK_INPUT_OUTPUT;
//设置窗口的大小和坐标
attr.x = widget->allocation.x;
attr.y = widget->allocation.y;
attr.width = widget->allocation.width;
attr.height = widget->allocation.height;


可有可无的有:

//colormap和visual,这两个是和显示与色彩有关的属性
attr.visual = gtk_widget_get_visual (widget);
attr.colormap = gtk_widget_get_colormap (widget);
//以及接收的事件
attr.event_mask = gtk_widget_get_events (widget);
attr.event_mask |= (GDK_BUTTON_MOTION_MASK |
                    GDK_ENTER_NOTIFY_MASK |
                    GDK_LEAVE_NOTIFY_MASK |
                    GDK_BUTTON_PRESS_MASK |
                    GDK_BUTTON_RELEASE_MASK |
                    GDK_EXPOSURE_MASK);

于是attr_mask一般为:

attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

表示创建时需要考虑x、y、visual、colormap的值(其他的是一定会考虑的,这也说明了这四个值是可有可无的)

为了使创建的窗口是一个顶层窗口,我们必须得到根窗口作为它的父窗口:
parent_window = gtk_widget_get_root_window (widget);
//如果只使用GDK的话可以用gdk_get_default_root_window来获取根窗口

设置好了属性之后就可以创建窗口了:

widget->window = gdk_window_new (parent_window, &attr, attr_mask);
//widget->window 可以换成其他GdkWindow*类型的变量

在成功创建一个顶层可见窗口后,再来看看透明窗口怎么弄的。要让一个窗口不可见,只需要把wclass属性改为GDK_INPUT_ONLY就行了。GDK_INPUT_ONLY表示这个窗口只接受用户交互事件,不会在屏幕上绘制任何东西。由于不会显示任何东西,visual和colormap属性也没有用了,attr_mask只需要为GDK_WA_X | GDK_WA_Y。

这样修改了之后,对于子窗口是没问题的,但是对于顶层窗口就不对劲了。如果把上面的代码这作出这些修改,在compiz下运行时会发现窗口会略往下偏。改用metacity下更严重,窗口的装饰(也就是边框啊标题栏啊什么的)被显示出来了,而且任务栏上有这个窗口的相应图标。首次移动后位置也不对。这样是完全不能达到我的要求的。

翻了翻GTK的文档,发现了这么一个控件:GtkInvisible,就是一个透明的窗口。查看源代码发现对于顶层窗口,还需要设置override_redirect为TRUE,同时attr_mask也要相应加上GDK_WA_NOREDIR,即:

attr.wclass = GDK_INPUT_ONLY;
attr.override_redirect = TRUE;
attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;

改成这样后,就可以正常使用顶层透明窗口了。override_redirect指定窗口管理器忽略这个窗口,这样就不会附加窗口装饰,也不会显示在任务栏上了。

 

程序设计 Comments(2) 2009年4月26日 10:13

实现背景透明的GTK+ widget

如果要用 GTK+ 写一些自定义界面的程序(如QQ),免不了要实现一个背景透明的 widget。一个简单的办法是GtkEventBox+GtkImage,不过这种方法太丑陋了点。然而用通常的方法来创建GTK+ widget 的话,会自动给背景填上颜色,设置了  gdk_window_set_back_pixmap (window->window, NULL, FALSE); 也不行。

继续阅读

未分类 Comments(0) 2009年2月11日 02:42