http://blog.csdn.net/cuijpus/article/details/4420033

GDK是gtk的下面一层的东西,平常都用GTK的函数,很少直接调用GDK的函数。但是GTK里面的事件、信号、回调、绘制等都和GDK有直接的关系,所以需要把GDK的绘图与事件相关的内容再理一理。

我原本打算自己去分析的,baidu了几下后,我放弃了,有分析的很好的文章有助于理解GDK的事件和绘图,下面几个是不错的分析,你们可以点击链接去看。后面我也放了几个汇总,方便些。第5部分的网友分析的非常不错。

1. GDK基础:

http://hi.baidu.com/phoenix20080808/blog/item/d2515519ec73f64043a9ada6.html

2. 图形上下文

http://blog.csdn.net/hahapro/archive/2007/07/25/1708132.aspx

3. GDK 事件类型

http://www.huihoo.org/gnu/gtk2-tut/a2711.html

4. GTK的消息流说明(X Window做后端的情况)

http://blog.csdn.net/absurd/archive/2006/03/08/619170.aspx

5. Gtk事件与信号关系

http://blog.csdn.net/c_spark/archive/2009/05/06/4155958.aspx

1. GDK基础

1 颜色表和颜色

1.1 颜色表

在X窗口系统中,像素值代表在一个颜色查找表中的入口,这是为了使系统能够具有多种显示模式(8位,16位)。例如:考虑一种八位的显示模式:八位不足以做为显示中的颜色编码,只能为很少部分的RGB值编码,显示时,系统将像素值用作索引,从颜色表中找出其对应的RGB值来显示像素。这个颜色表称为colormap。有时,我们可以修改颜色表以包含要使用的颜色(一些colormap是只读的,不能被修改)。在Gdk中,用GdkColormap来表示一个颜色表

  1. Gdk_widget_get_colormap:获取指定构件的颜色表
  2. Gdk_colormap_get_system:获取系统的缺省颜色表

1.2 颜色值

GDK使用GdkColor存储RGB值和像素值。红、绿、蓝值是以 1 6位无符号整数给出的,取值范围为0到65535GdkColor的定义如下:

struct GdkColor
{
   gulong pixel; 像素值
   gushort red;
   gushort green;
   gushort blue;
};

在用一种颜色绘画时,必须保证:

  1. 保证像素值包含合适的值
  2. 保证颜色值在要使用的可绘区的颜色表中存在

例题:使用指定的颜色绘画的步骤

GdkColor color;

color.red = 65535;
color.green = 0;
color.blue = 0;

if (gdk_colormap_alloc_color(colormap, &color, FALSE, TRUE)) //填充GdkColor的pixel分量 
{
/* 成功!*/
}
其中,colormap为要绘画的可绘区的颜色表,此后,就可以使用该GtkColor颜色来绘画了

  1. gdk_colormap_alloc_color:根据GdkColor结构中的红、绿、蓝分量的值来查找颜色表,从中找出该红、绿、蓝分量对应的像素值并填充GdkColor的pixel成员。
  2. 使用完一种颜色后,应该用 gdk_colormap_free_colors()函数将它从颜色表中删除
  3. gdk_color_parse:从颜色的名字来获取颜色对应的RGB值并填充指定的GdkColor变量

2 可绘区和pixmap

2.1 定义

  1. pixmap指的是内存中的一块图片缓冲区,可以在里面绘图,然后将该pixmap中的数据复制到显存中,这样做可以快速更新屏幕。因此,pixmap一般用来存储从磁盘加载的图像数据。在Gdk中,使用GdkPixmap来表示一个pixmap
  2. 位图指的是深度为1的pixmap,在Gdk中,使用GdkBitmap来表示一个位图
  3. 可绘区指的是可以用来绘图的区域,在Gdk中,使用GdkDrawable来表示一个可绘区。可绘区包括GdkWindow、Gdkpixmap以及GdkBitmap(深度为1的pixmap)

2.2 操作

  1. gdk_pixmap_new(GdkDrawable*drawable,gint width,gint height,gint depth):创建一个pixmap
    创建pixmap时,应该使pixmap的色深与它要绘制到的目标窗口的色深相同。
    如果在其drawable参数中指定一个窗口,则色深将与指定窗口的色深相同,此时应该将depth参数设置为-1。
    如果drawable参数为NULL,则必须在其depth参数中指定色深

    ================================================================================================

  2. gdk_pixmap_unref:销毁一个pixmap
  3. gtk_pixmap_new:使用指定的GdkPixmap来创建构件

2.3 GdkPixmap的使用

GtkWidget* parent;
gchar *xpm_filename;
GdkPixmap *pixmap;
GdkBitmap *mask;
GtkStyle *style;

创建GtkPixmap要复制到的目标构件。注意:必须实现该构件才能使用它的window属性
parent=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_realize(parent);

style=gtk_widget_get_style(parent);
pixmap=gdk_pixmap_create_from_xpm(parent->window,&mask,&style->bg[GTK_STATE_NORMAL],xpm_filename);


3 鼠标指针

  1. gdk_window_get_pointer用来获取鼠标指针的坐标
  2. gdk_pointer_grab函数用来让一个窗口独占指针
  3. gdk_pointer_is_grabbed函数用来判断鼠标是否被独占了
  4. gdk_pointer_ungrab用来取消对鼠标指针的独占
    ==========================================================

在程序中改变鼠标指针的方法
GdkCursor* cursor;
cursor = gdk_cursor_new(GDK_CLOCK);
gdk_window_set_cursor(window, cursor);
gdk_cursor_destroy(cursor);
注意

2. 图形上下文:

一个图形上下文,或者GC(GraphicsContext),是一套在绘图时要用到的参数(比如颜色、剪裁屏蔽值、字体等等)。它是一种服务器端资 源,就像pixmap和窗口一样。GC减少了Gdk绘图函数的参数个数,也减少了每个绘图请求从客户到服务器间传递的参数的数目。

   与GdkWindowAttr类似,图形上下文可以用GdkGCValues结构创建。结构中包含了图形上下文中所有的特性,还可以传递 gdk_gc_new_with_values()标志指出哪一个成员是有效的。其他的成员保留其缺省值。还可以用gdk_gc_new()函数(这种方 法通常更容易)创建一个全为缺省值的GC。创建GC之后,还有一些函数用来改变GC的设置,但是要记住,每次改变GC设置值都需要一条消息传递到X服务 器。

  所有的GC都是不可以互换的,它们都与特定的深度和视件相关联。GC的深度和视 件必须与要绘图的可绘区的深度和视件相匹配。GC的深度和视件是从传递到gdk_gc_new()函数的GdkWindow*参数中获得的,所以处理这种 问题的最容易的方法就是在要绘图的窗口上创建GC。

  下面是GdkGCValues结构的定义:

  typedefstruct_GdkGCValuesGdkGCValues;

  struct_GdkGCValues

  {

   GdkColorforeground;

   GdkColorbackground;

   GdkFont*font;

   GdkFunctionfunction;

   GdkFillfill;

   GdkPixmap*tile;

   GdkPixmap*stipple;

   GdkPixmap*clip_mask;

   GdkSubwindowModesubwindow_mode;

   gintts_x_origin;

   gintts_y_origin;

   gintclip_x_origin;

   gintclip_y_origin;

   gintgraphics_exposures;

   gintline_width;

   GdkLineStyleline_style;

   GdkCapStylecap_style;

   GdkJoinStylejoin_style;

  };

  前景色(foreground成员)是画线、圆或其他形状时的“画笔颜色”。背景色(background成员)的用处依赖于特定的绘画操作。这些颜色必须是用gdk_color_alloc()函数在当前颜色表中分配的。

   font成员没有用到:在Xlib中绘制文本时用它指定字体。在Gdk以前的版本中,它也有同样的作用;但是新的绘制文本的Gdk程序都要求一个 GdkFont*参数。一个Xlib图形上下文只能存储无格式的字体,但是GdkFont能够代表一个字体集(用以绘制一些外语文字)。

  function成员指定要画的像素点与可绘区上已有的像素点如何结合起来。有许多种可能取值,但是只有两种是最常用的:

  GDK_COPY:缺省值。它忽略已存在的像素点(只是将新的像素点画在上面)。

  GDK_XOR:将旧的和新的像素点一种可反转的方式结合起来。也就是,如果执行两次GDK_OR操作,头一次绘图就会被第二次操作取消。GDK_XOR通常用于“擦除”,可以恢复可绘区原来内容。

   GdkGCValues的fill成员决定如何使用GdkGCValues中的tile和stipple成员。其中tile成员是一个与目的可绘区深度 相同的pixmap图片,它被反复复制到目的可绘区,将它们拼贴起来—第一次拼贴的原点是(ts_x_origin,ts_y_origin)。而 stipple成员是一个位图(深度为1的pixmap);它也是从(ts_x_origin,ts_y_origin)开始拼贴的。下面是fill的可 能取值:

  GDK_SOLID:忽略tile和stipple成员。绘图形状是用前景色和背景色绘制的。

  GDK_TILED:绘图形状用tile成员指定的pixmap图片绘制,而不是用前景色和背景色。用GDK_TILED模式绘画会擦除可绘区上的任何内容,显示由tile成员指定的图片的拼贴图形。

  GDK_STIPPLED:用stipple中定义的位绘制图形。也就是,在stipple成员中未设置的位不会绘出。

  GDK_OPAQUE_STIPPLED:用前景色绘制在stipple中设置的位,没有在stipple中设置的位用背景色绘制。

有些X服务器并没有有效实现上面所有的fill模式值,所以使用时可能会很慢。clip_mask成员是可选的,它是一个位图,只有在这个位图中设置了的 位才会画出。从clip_mask到可绘区的映射是由clip_x_origin和clip_y_origin值决定的,这些定义了与clip_mask 中(0,0)对应的可绘区的坐标。也可以设置一个剪裁矩形(最常用的、也是最有用的形式)或一个剪裁区域(区域就是在屏幕上的任意范围,典型情况是一个多 边形或矩形列表)。

  用gdk_gc_set_clip_rectangle()设置剪裁矩形:

  GdkRectangleclip_rect;

  clip_rect.x=10;

  clip_rect.y=20;

  clip_rect.width=200;

  clip_rect.height=100;

  gdk_gc_set_clip_rectangle(gc,&clip_rect);

   要关闭“剪裁”,将剪裁的矩形、区域或剪裁屏蔽值设置为NULL。GC的subwindow_mode只与可绘区是否为一个窗口有关。缺省设置是 GDK_CLIP_BY_CHILDREN;这意指子窗口不会被在父窗口上的绘图影响。这会造成一个假象:子窗口在父窗口的“上面”,并且子窗口是不透明 的。GDK_INCLUDE_INFERIORS在所有在“上面”的

子窗口上绘制,改写子窗口上包含的任何图形—通常不使用这种模式。如果确实在使用GDK_INCLUDE_INFERIORS模式,可能要使用GDK_XOR作为绘图函数,因为它允许恢复子窗口原先的内容。

  graphics_exposures是一个布尔值,缺省是TRUE,它决定gdk_window_copy_area()是否产生expose事件。

GC的最后四个值决定怎样画线。这些值用于画线,包括未填充多边形的边框以及弧线。line_width域决定线的宽度(以像素计)。宽度为0的线称为一 条“细线”,细线是一个像素宽的线,绘制得非常快(通常使用硬件加速),但是画的具体像素依赖于所使用的X服务器。为了一致性,最好使用宽度为1的线。

  line_style域可以是下面三种值:

  GDK_LINE_SOLID是缺省值;一条实线。

  GDK_LINE_ON_OFF_DASH用前景色画一条虚线,将虚线的off(关闭)部分空着。

  GDK_LINE_DOUBLE_DASH用前景色画一条虚线,但是虚线的off(关闭)部分用背景色绘制。虚线是gdk_gc_set_dashes()指定的;GdkGCValues中并不包括这个域。gdk_gc_set_dashes()需要三个参数:

  dash_list是一个虚线长度的数组。偶数号的长度是“on”(打开)部分,它们是用前景色绘制的;奇数号的长度是“off”(关闭)部分,它们不画出,或者用背景色绘制,具体绘制方法依赖于line_style。长度值不能是0,所有的值必须是正数。

  dash_offset是在虚线列表中第一个像素的索引号。也就是,如果在dash_list中指定了5个on和5个off,并且dash_offset是3,绘制的线将从第3个on虚线开始。

  N是在dash_list中的元素的个数。可以设置一个古怪的虚线模式,例如:

  gchardash_list[]={5,5,3,3,1,1,3,3};

  gdk_gc_set_dashes(gc,0,dash_list,sizeof(dash_list));

   缺省的dash_list是{4,4},偏移量是0。图16-1显示了一些用GDK_LINE_DOUBLE_DASH画的虚线。图形上下文的前景色是 黑色,背景色是亮灰色。头5根线是缺省的{4,4}虚线模式,偏移量分别是0、1、2、3和4。记住,缺省值是0。图16-2显示了这5根线的放大图。最 后的一根线就是上面提到的古怪虚线模式,它的放大图显示在图16-3。

  cap_style决定X怎样画线的端点(或虚线端点)。它有4种可能取值:

  GDK_CAP_BUTT:是缺省值,它意味着线的端点是正方形的。

  GDK_CAP_NOT_LAST:对应一个像素宽度的线,最后一个像素忽略不画。其他与GDK_CAP_BUTT一样。

  GDK_CAP_ROUND:在线的端点画一个小弧线,由线的端点向两边延伸。弧线的中心是线的端点,半径是线宽的一半。对一个像素宽的线,它没有什么效果(因为没有办法画一个像素宽的弧线)。

  GDK_CAP_PROJECTING:将线延伸,越过它的终点半个线宽。它对一个像素的线没有效果。

  join_style参数影响画多边形或在一个函数中画多条线时,各线之间如何连接。如果把线想象成一个细长的矩形,就很容易弄清楚线之间并不是平滑 连接的。在连接的两个端点之间有一个凹槽。对这个凹槽有三种处理方法,也就是join_style的三种可能取值:

  GDK_JOIN_MITER:是缺省值,在线交叉的地方画一个尖角。

  GDK_JOIN_ROUND:在交叉的凹槽处画一个弧线,画一个圆形的转角。

  GDK_JOIN_BEVEL:用最小的可能的形状填充凹槽,画一个平坦的转角。函数列表:GdkGC

  #include<gdk/gdk.h>

  GdkGC*gdk_gc_new(GdkWindow*window)

  GdkGC*gdk_gc_new_with_values(GdkWindow*window,

   GdkGCValues*values,

   GdkGCValuesMaskvalues_mask)

  voidgdk_gc_set_dashes(GdkGC*gc,

   gintdash_offset,

   gchardash_list,

   gintn)

  voidgdk_gc_unref(GdkGC*gc)

5. Gtk事件与信号关系

--分析中相关的源码包 gtk+2.0-2.12.9

Gtk所提供的工具库与Gtk应用程序与都是基于事件触发机制来管理。所有的Gtk应用程序都是基于事件驱动,如果没有事件发生,应用程序将处于等待状态,不会执行任何操作,一旦事件发生,将根据不同的事件做出相应的处理。在GTK+中,一个事件就是从X Server传出来的一个信息。当一个事件发生时,Gtk程序就会通过发送一个信号来通知应用程序执行相关的操作,即调用指定控件与这一信号进行绑定的回调函数,来完成一次由事件所触发的行动。

一、通过对源代码分析,解决理解过程中所产生的问题:

1、Gtk应用程序如何能接收到来至XServer端的事件:

根据以前的分析可知,GDK层所提供的事件循环对XServer端传过来的事件进行管理,并将其转化为GDK层的事件,通过对具体源代码的分析,可以知道Gtk应用程序能接收的事件,便是来自GDK层进行加工过的Gdk事件。

在程序中可以得知,当有事件需要处理时,Gdk层对XServer端的事件以队列的形式进行管理(_gdk_events_queue),并将X事件通过(gdk_event_translate)转换为GDK事件,将转换后的事件放入队列中,每次从队列中取出队首的事件(_gdk_event_unqueue),如果事件不为空,这时程序中的处理是将GDK事件GdkEvent交给了_gdk_event_func函数进行处理,到这我们可以知道,由XServer端产生的事件经过GDK层后将事件由一个函数_gdk_event_func来处理。

进一步分析知道,_gdk_event_func这一函数指针做为一个GDK提供给外部的一个接口,外部程序如果实现这一接口,便可以接收来自GDK层转换XServer端产生的事件,在对Gtk的分析过程中,该接口是由Gtk来实现(gtk_main_do_event),也就是Gtk程序通过这一函数来处理事件。

当程序在调用gtk_init时对_gdk_event_func进行设置。

2、Gtk应用程序接收到事件后如何对事件进行处理:

在进一步的分析中,Gtk应用程序采用了信号的机制,通过信号的方式,通知Gtk其它的控件做出相应的动作。每一个控件都有自己定义的信号,每一个信号都可以绑定到一个指定的回调函数上。

从gtk_main_do_event函数中,通过调用内部函数(如:gtk_widget_event_internal)将GDK事件关联到Gtk自身定义的信号上,并将信号发出,绑定这一信号的控件便可以接收到,这样根据信号的定义执行相应的回调函数。

二、以对GtkButton控件的点击事件(press-release-click)为例,进行分析:

当鼠标点击在GtkButton上时,系统做出了以下的反应:

由于XServer实时接收着来至输入设备的操作,当鼠标点击后,XServer立即生成一个XEvent结构,里面描述着所点击的控件,XEvent产生的事件类型等具体信息,这时将这一事件传给GDK层,GDK层通过将XEvent事件转化为GdkEvent,将转化后的事件交给函数接口(_gdk_event_func)处理,即gtk_main_do_event。经过几层函数调用的处理,在函数gtk_widget_event中调用gtk_widget_event_internal,将事件转化成为Gtk中的信号类型,并通过g_signal_emit将GtkButton的信号BUTTON_PRESS_EVENT发送出去。

在gtkwindow.c文件中,对GtkWidgetClass创建了BUTTON_PRESS_EVENT、BUTTON_RELEASE_EVENT等类型信号,BUTTON_PRESS_EVENT与 函数button_press_event关联,GtkButton类型继承了父类GtkWidget的信号,并将函数button_press_event进行重载为gtk_button_button_press。在GtkButton结构中定义自己的信号"PRESSED",关联函数为gtk_real_button_pressed。

在GtkButton中定义了与单击按钮时关联的信号类型为: PRESSED,RELEASED,CLICKED。

通过分析源码,信号的流程是在gtk_widget_event_internal函数中所发出的信号由GtkWidget所接收(所有widget的父类,如GtkButton), 再由GtkWidget中对这一信号所关联的回调函数(gtk_button_button_press),来发出由GtkWidget子类所定义的信号,最终的操作在于GtkWidget子类中信号的定义。即最终信号的发出是gtk_button_pressed调用g_signal_emit将“PRESSED”信号发出,调用回调函数gtk_real_button_pressed修改GtkButton的属性。

以同样的方式,处理鼠标对按钮的释放(release)事件,当RELEASED信号发出后,调用与RELEASED信号相关联的回调函数,根据当前GtkButton的属性进行判断如果当前按钮满足一定的条件,如当前按钮是否处于按下(down)状态等,则由gtk_button_clicked,发出一个"CLICKED"信号,调用与"CLICKED"关联的回调函数执行click事件的操作。

三、参考资料

http://library.gnome.org/devel/gtk-tutorial/stable/

No.Starch.Press.The.Official.GNOME.2.Developers.Guide.eBook-LiB.pdf

借花献佛,我做个总结:

1. 程序初始化时,放个钩子下去,钓鱼

2. 有按键后,由Xserver/或者DirectFB Master层传到GDK层

3. GDK层把X/DFB事件转换为GDK事件

4. GTK层把GDK事件转换为Gtk信号,并发出去

5. 然后GObject会调用Gtk信号对应的回调函数,完成对事件的响应

6. 在回调函数里面,有可能做些界面更新的操作

GDK事件与GTK signal的前世今生相关推荐

  1. 【GTK】信号量(signal)大全

    信号函数: gint gtk_signal_connect(GtkObject *object,gchar *name,GtkSignalFuncfunc,gpointer func_data); 回 ...

  2. 使用GTK+和Glade快速开发Linux图形界面

    * 使用 GTK+ 和 Glade 快速开发 Linux 图形界面 ** GTK+ 简介 *** 基本概念 GTK+ 是一种用于创建图形界面的库. 嗯, gnome 用的就是它, 不过并不是说只 有在 ...

  3. C语言基于GTK+Libvlc实现的简易视频播放器(二)

    简易视频播放器-全屏播放 一.课程说明 上一次我们使用gtk+libvlc实现了一个最简单的视频播放器,可以实现点击按钮暂定和停止播放视频,以及同步显示视频播放进度,但即使作为一个视频播放器,只有这些 ...

  4. 自定义添加的鼠标事件

    当鼠标在屏幕上移动时,可以使用鼠标移动事件跟踪它的移动.移动事件是当鼠标指针在窗口内移动时发生的,穿越事件是在鼠标指针进入或离开GdkWindow窗口时发生的.移动事件中的典型成员是GDK_MOTIO ...

  5. Glib学习(22) 主事件循环 The Main Event Loop

    glib源码下载:http://ftp.gnome.org/pub/gnome/sources/glib/ glib帮助文档:https://developer.gnome.org/glib/ 主事件 ...

  6. python signal

    参数 描述 signal.SIG_DFL 执行信号的默认函数 signal.SIG_IGN 忽略信号 signal.NSIG signal.ITIMER_REAL signal.ITIMER_VIRT ...

  7. GTK测试程序(十四)

    0.安装gtk安装包 # sudo apt-get install libgtk2.0-dev libgtk-3-dev 1.gtk_demo.c #include <string.h> ...

  8. 【activiti 入门】activiti6.0之事件总结

    从结构上来讲如下图: 事件定义     定时器事件定义     错误事件定义     信号事件定义     消息事件定义 开始事件     空开始事件     定时开始事件     消息开始事件    ...

  9. QToolButton双击事件

    QToolButon自身信号不具有双击事件,如果想要实现双击事件,需要重写QToolButton. 首先需要继承QToolButtton,利用鼠标双击事件void mouseDoubleClickEv ...

  10. pyQt5中单击、双击、拖动事件区分

    一般在处理单击.双击.拖动事件时会遇到以下问题: 双击时会产生两个额外的单击事件和两个释放事件 拖动时会在拖动开始处产生一个单击事件,在拖动结束时产生一个释放事件 故采用signal作为事件类型的标志 ...

最新文章

  1. vscode配置C/C++ windows编译环境。
  2. UVA10763交换学生
  3. 三级网络技术_三级网络技术考前选择题—VLAN
  4. 关于c++ pair自己遇到的一个问题?为何不一样?为何第一个程序不能返回pair内数组的值呢?(已经解决)
  5. 微信小程序实现数字为四位一组间隔(仿银行卡卡号)
  6. linux 设备 major 253,redhat5.5测试环境中使用udev配置raw设备
  7. mysql sharding 方案_mysql sharding 方案 分库分表(sharding)系列(4)
  8. redis主线程阻塞的情形
  9. Java——JVM基本原理
  10. [VC] 【Visual Studio】2005~2015中文完整旗舰版(附序列号)
  11. vue click 跳转外部链接
  12. PO(PageObject)模型
  13. 多功能聊天室 第一次迭代总结与第二次迭代计划
  14. Ant Design Vue 组件或图标的引入和使用
  15. 【期末复习】操作系统
  16. Ubuntu14.04 安装 TP_LINK驱动 TL-WDN5200H 1.0无线网卡
  17. 汇编:裴波那契数列前50项
  18. 【乐逍遥网站设计】网站建设如何让网站设计更加合理化?
  19. Xilinx FPGA提供DDR4内存接口解决方案
  20. windows 系统错误码总结

热门文章

  1. 【笔试】中移物联网重庆公司
  2. 安装python3.5.0出现0x80072eff错误_我在网上用WINDOWS UPDATE更新WINDOWS,出现错误:0x80072EFF,该怎么办?...
  3. 下载安装Tomcat8.5.30
  4. 带你深入了解Web3开发者堆栈
  5. win10 net framework 3.5提示错误代码0x800f081f
  6. 你真的知道如何正确清除 DNS 缓存吗?( 附全平台详细教程 )
  7. android设备如何挂载sdcard(有些设备是自动挂载的)
  8. 微信开发、申请微信号测试账号
  9. 浅谈OSPF的DRBDR
  10. 中国天然大豆卵磷脂市场趋势报告、技术动态创新及市场预测