DirectFB作为GDK后端时,gtk_widget_show_all()的内部机制

下载一个gtk,directfb源码,

http://www.gtk.org/

http://www.directfb.org/

 

就可以分析出其复杂的饶人的重绘过程。

不少用GTK做程序的人,知道用gtk_widget_show_all来显示,内部的现实过程如果比较清楚,分析问题的能力应该会好点。

 

程序都是调用这个接口: void

gtk_widget_show_all (GtkWidget *widget)

{

  GtkWidgetClass *class;

 

  g_return_if_fail (GTK_IS_WIDGET (widget));

 

  if ((GTK_WIDGET_FLAGS (widget) & GTK_NO_SHOW_ALL) != 0)

    return;

 

  class = GTK_WIDGET_GET_CLASS (widget);

 

  if (class->show_all)

    class->show_all (widget);

}
 


 

获取其class,然后调用其注册的show_all函数:  

static void

gtk_widget_class_init (GtkWidgetClass *klass)

{

 ……

klass->show = gtk_widget_real_show;

  klass->show_all = gtk_widget_show;

  klass->hide = gtk_widget_real_hide;

  klass->hide_all = gtk_widget_hide;

  klass->map = gtk_widget_real_map;

  klass->unmap = gtk_widget_real_unmap;

  klass->realize = gtk_widget_real_realize;

  klass->unrealize = gtk_widget_real_unrealize;
 


 

 

把一个widget设置为可见,并且发送SHOW信号出去 void

gtk_widget_show (GtkWidget *widget)

{

  if (!GTK_WIDGET_VISIBLE (widget))

    {

      g_object_ref (widget);

      if (!GTK_WIDGET_TOPLEVEL (widget))

             gtk_widget_queue_resize (widget);

      g_signal_emit (widget, widget_signals[SHOW], 0);

      g_object_notify (G_OBJECT (widget), "visible");

      g_object_unref (widget);

    }

}
 


 

看这个SHOW信号的回调:

klass->show = gtk_widget_real_show;

  static void

gtk_widget_real_show (GtkWidget *widget)

{

  if (!GTK_WIDGET_VISIBLE (widget))

    {

      GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);

 

      if (widget->parent &&

               GTK_WIDGET_MAPPED (widget->parent) &&

               GTK_WIDGET_CHILD_VISIBLE (widget) &&

               !GTK_WIDGET_MAPPED (widget))

             gtk_widget_map (widget);

    }

}
 


 

 

  void

gtk_widget_map (GtkWidget *widget)

{

  g_return_if_fail (GTK_IS_WIDGET (widget));

  g_return_if_fail (GTK_WIDGET_VISIBLE (widget));

  g_return_if_fail (GTK_WIDGET_CHILD_VISIBLE (widget));

 

  if (!GTK_WIDGET_MAPPED (widget))

    {

      if (!GTK_WIDGET_REALIZED (widget))

             gtk_widget_realize (widget);

 

      g_signal_emit (widget, widget_signals[MAP], 0);

 

      if (GTK_WIDGET_NO_WINDOW (widget))

             gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);

    }

}
 


 

 

如果一个widget没有被realize, 则先realize它。

  
 


 

所谓的realize: 就是给一个widget 创建一个GDK的资源,比如gdk window

这里的realize可能是递归的过程,一直把它的Parent widget都给创建一个gdk资源。

Realize完后,就发送一个realize信号。

然后调用其回调:

klass->realize = gtk_widget_real_realize;

 

处理完后,再发送一个MAP信号:

调用其回调:

klass->map = gtk_widget_real_map; static void

gtk_widget_real_map (GtkWidget *widget)

{

  g_assert (GTK_WIDGET_REALIZED (widget));

 

  if (!GTK_WIDGET_MAPPED (widget))

    {

      GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);

     

      if (!GTK_WIDGET_NO_WINDOW (widget))

             gdk_window_show (widget->window);

    }

}
 


 

//map 是开始显示的过程。

这个gdk_window_show()有几个版本:Directfb,X11, etc.

这里我们用Directfb的版本: void

gdk_window_show (GdkWindow *window)

{

  g_return_if_fail (GDK_IS_WINDOW (window));

 

  show_window_internal (window, TRUE);

}
 


 

Show_window_internal()是需要研究的重点:

 

Gdkwindow-directfb.c 下面的这个函数

  1 把窗口放到顶层

2 给该window内的所有的小窗口发送map事件?

3 gdk_window_invalidate_rect

  // 该函数让窗口绘图区域失效,并产生窗口重绘制事件(即 expose 事件)。

4 还可以给窗口设置透明
 


 

 

接着看: void

gdk_window_invalidate_rect   (GdkWindow    *window,

                                              GdkRectangle *rect,

                                              gboolean      invalidate_children)

{

  GdkRectangle window_rect;

  GdkRegion *region;

  GdkWindowObject *private = (GdkWindowObject *)window;

 

  g_return_if_fail (window != NULL);

  g_return_if_fail (GDK_IS_WINDOW (window));

 

  if (GDK_WINDOW_DESTROYED (window))

    return;

 

  if (private->input_only || !GDK_WINDOW_IS_MAPPED (window))

    return;

 

  if (!rect)

    {

      window_rect.x = 0;

      window_rect.y = 0;

      gdk_drawable_get_size (GDK_DRAWABLE (window),

                             &window_rect.width,

                             &window_rect.height);

      rect = &window_rect;

    }

 

  region = gdk_region_rectangle (rect);

  gdk_window_invalidate_region (window, region, invalidate_children); //重画失效区

  gdk_region_destroy (region);

}
 


 

Go on : 这里的注释要好好理解一下。

主要是递归提取需要更新的窗口,放到一个全局链表中 update_windows ,

  /**

 * gdk_window_invalidate_maybe_recurse:

 * @window: a #GdkWindow

 * @region: a #GdkRegion

 * @child_func: function to use to decide if to recurse to a child,

 *              %NULL means never recurse.

 * @user_data: data passed to @child_func

 *

 * Adds @region to the update area for @window. The update area is the

 * region that needs to be redrawn, or "dirty region." The call

 * gdk_window_process_updates() sends one or more expose events to the

 * window, which together cover the entire update area. An

 * application would normally redraw the contents of @window in

 * response to those expose events.

 *

 * GDK will call gdk_window_process_all_updates() on your behalf

 * whenever your program returns to the main loop and becomes idle, so

 * normally there's no need to do that manually, you just need to

 * invalidate regions that you know should be redrawn.

 *

 * The @child_func parameter controls whether the region of

 * each child window that intersects @region will also be invalidated.

 * Only children for which @child_func returns TRUE will have the area

 * invalidated.

 **/

void

gdk_window_invalidate_maybe_recurse (GdkWindow *window,

                                                           GdkRegion *region,

                                                           gboolean (*child_func) (GdkWindow *, gpointer),

                                                           gpointer   user_data)

{

  GdkWindowObject *private = (GdkWindowObject *)window;

  GdkRegion *visible_region;

  GList *tmp_list;

 

  g_return_if_fail (window != NULL);

  g_return_if_fail (GDK_IS_WINDOW (window));

 

  if (GDK_WINDOW_DESTROYED (window))

    return;

 

  if (private->input_only || !GDK_WINDOW_IS_MAPPED (window))

    return;

 

  if (GDK_IS_PAINTABLE (private->impl) &&

      GDK_PAINTABLE_GET_IFACE (private->impl)->invalidate_maybe_recurse)

    {

      GDK_PAINTABLE_GET_IFACE (private->impl)->invalidate_maybe_recurse (GDK_PAINTABLE (private->impl), region,

                                                                                                                          child_func, user_data);

      return;

    }

 

  visible_region = gdk_drawable_get_visible_region (window);

  gdk_region_intersect (visible_region, region);

 

  tmp_list = private->children;

  while (tmp_list)

    {

      GdkWindowObject *child = tmp_list->data;

     

      if (!child->input_only)

             {

               GdkRegion *child_region;

               GdkRectangle child_rect;

              

               gdk_window_get_position ((GdkWindow *)child,

                                                         &child_rect.x, &child_rect.y);

               gdk_drawable_get_size ((GdkDrawable *)child,

                                                       &child_rect.width, &child_rect.height);

 

               child_region = gdk_region_rectangle (&child_rect);

              

               /* remove child area from the invalid area of the parent */

               if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped)

                 gdk_region_subtract (visible_region, child_region);

              

               if (child_func && (*child_func) ((GdkWindow *)child, user_data))

                 {

                   gdk_region_offset (region, - child_rect.x, - child_rect.y);

                   gdk_region_offset (child_region, - child_rect.x, - child_rect.y);

                   gdk_region_intersect (child_region, region);

                  

                   gdk_window_invalidate_maybe_recurse ((GdkWindow *)child,

                                                                                   child_region, child_func, user_data);

                  

                   gdk_region_offset (region, child_rect.x, child_rect.y);

                 }

 

               gdk_region_destroy (child_region);

             }

 

      tmp_list = tmp_list->next;

    }

 

  if (!gdk_region_empty (visible_region))

    {

      if (debug_updates)

        draw_ugly_color (window, region);

     

      if (private->update_area)

             {

               gdk_region_union (private->update_area, visible_region);

             }

      else

             {

               update_windows = g_slist_prepend (update_windows, window);

               private->update_area = gdk_region_copy (visible_region);

              

               gdk_window_schedule_update (window);

             }

    }

 

  gdk_region_destroy (visible_region);

}
 


 

 

接着会注册一个回调, 在glib main loop空闲时,更新绘制 static void

gdk_window_schedule_update (GdkWindow *window)

{

  if (window && GDK_WINDOW_OBJECT (window)->update_freeze_count)

    return;

 

  if (!update_idle)

    {

      update_idle = g_idle_add_full (GDK_PRIORITY_REDRAW,

                                                           gdk_window_update_idle, NULL, NULL);

    }

}
 


  static gboolean

gdk_window_update_idle (gpointer data)

{

  GDK_THREADS_ENTER ();

 gdk_window_directfb_process_all_updates ();//这个地方可以调用directfb的后端也可以调用TinyX的后端

  GDK_THREADS_LEAVE ();

 

  return FALSE;

}
 


 

 

根据前面链表中的需要更新的窗口,进行更新: void

gdk_window_directfb_process_all_updates (void)

{

GSList *old_update_windows = update_windows;

  GSList *tmp_list = update_windows;

 

  if (update_idle)

    g_source_remove (update_idle);

 

  update_windows = NULL;

  update_idle = 0;

 

  g_slist_foreach (old_update_windows, (GFunc)g_object_ref, NULL);

 

  while (tmp_list)

    {

      GdkWindowObject *private = (GdkWindowObject *)tmp_list->data;

 

      if (private->update_freeze_count)

             update_windows = g_slist_prepend (update_windows, private);

      else

             gdk_window_process_updates(tmp_list->data,TRUE);

 

      g_object_unref (tmp_list->data);

      tmp_list = tmp_list->next;

}

 

//

 

  g_slist_free (old_update_windows);

 

  flush_all_displays ();

}
 


 

 

主要是让window响应gdk_expose事件,在实践处理函数里面,进行窗口的重新绘制。

   

static void

gdk_window_process_updates_internal (GdkWindow *window)

{

  GdkWindowObject *private = (GdkWindowObject *)window;

  gboolean save_region = FALSE;

 

  /* If an update got queued during update processing, we can get a

   * window in the update queue that has an empty update_area.

   * just ignore it.

   */

  if (private->update_area)

    {

      GdkRegion *update_area = private->update_area;

      private->update_area = NULL;

     

      if (_gdk_event_func && gdk_window_is_viewable (window))

             {

               GdkRectangle window_rect;

               GdkRegion *expose_region;

               GdkRegion *window_region;

          gint width, height;

 

          if (debug_updates)

            {

              /* Make sure we see the red invalid area before redrawing. */

              gdk_display_sync (gdk_drawable_get_display (window));

              g_usleep (70000);

            }

         

               save_region = _gdk_windowing_window_queue_antiexpose (window, update_area);

 

               if (save_region)

                 expose_region = gdk_region_copy (update_area);

               else

                 expose_region = update_area;

              

          gdk_drawable_get_size (GDK_DRAWABLE (private), &width, &height);

 

               window_rect.x = 0;

               window_rect.y = 0;

               window_rect.width = width;

               window_rect.height = height;

 

               window_region = gdk_region_rectangle (&window_rect);

               gdk_region_intersect (expose_region,

                                                      window_region);

               gdk_region_destroy (window_region);

              

               if (!gdk_region_empty (expose_region) &&

                   (private->event_mask & GDK_EXPOSURE_MASK))

                 {

                   GdkEvent event;

                  

                   event.expose.type = GDK_EXPOSE;

                   event.expose.window = g_object_ref (window);

                   event.expose.send_event = FALSE;

                   event.expose.count = 0;

                   event.expose.region = expose_region;

                   gdk_region_get_clipbox (expose_region, &event.expose.area);

                  

                   (*_gdk_event_func) (&event, _gdk_event_data);

                  

                   g_object_unref (window);

                 }

 

               if (expose_region != update_area)

                 gdk_region_destroy (expose_region);

             }

      if (!save_region)

             gdk_region_destroy (update_area);

    }

}
 


 

 

反过来查看一下那个全局事件响应函数:

啥时候注册的: void

gdk_event_handler_set (GdkEventFunc   func,

                                  gpointer       data,

                                  GDestroyNotify notify)

{

  if (_gdk_event_notify)

    (*_gdk_event_notify) (_gdk_event_data);

 

  _gdk_event_func = func;

  _gdk_event_data = data;

  _gdk_event_notify = notify;

}
 


  Gtkmain.c (gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
 


 

所以我们到gtk_main_do_event里面看看如何响应GDK_EXPOSE的: case GDK_EXPOSE:

//                        g_message("gtk_main_do_event(GDK_EXPOSE)/n");

      if (event->any.window && GTK_WIDGET_DOUBLE_BUFFERED (event_widget))

             {

               gdk_window_begin_paint_region (event->any.window, event->expose.region);  //开始画

               gtk_widget_send_expose (event_widget, event); //把GDK_EXPOSE事件转化为gtk_expose信号发送出去,这样gtk app就可以做些自己的事情了,如果app没有对gtk_expose事件响应,则忽略掉

               gdk_window_end_paint (event->any.window);

             }

      else

             gtk_widget_send_expose (event_widget, event);
 


 

到这里,gtk show的过程应该很清楚了。

 

总结一下:

show_all()会激发 "show" -> "realize" -> "map" 一系列的信号,

然后在glib idle时启动窗口的迭代绘制过程,完成窗口的现实。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cuijpus/archive/2009/08/02/4401687.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值