changeset 107887:948294352a25

Don't redraw lucid menus more than needed. Use double buffer to reduce flicker. * xlwmenu.c: Include Shell.h, remove duplicate declaration of XlwMenuRedisplay. (display_menu_item): Replace ws->window with ws->pixmap, remove call to XftDrawRect. (display_menu): Remove this and that argument. Remove just_compute_this_one_p. Fill pixmap at start and copy it to window at end. (expose_cb): New function. (make_windows_if_needed): Replace XCreateWindow with XtCreatePopup. Add eventhandler for expose to expose_cb. Remove creation of xft_draw. (create_pixmap_for_menu): New function. (remap_menubar): Pop down menus that aren't the same as in old_stack. Set width, heigh, x, y on widget with XtVaSetValues. Call create_pixmap_for_menu. Replace XUnmapWindow with XtPopdown. Remowe two last parameters to display_menu. (map_event_to_widget_value, XlwMenuRedisplay, Key, Select) (pop_up_menu): Remowe two last parameters to display_menu. (XlwMenuRealize): Call create_pixmap_for_menu, set w and pixmap. Remove call to XftDrawCreate. (XlwMenuDestroy): Free pixmap. Call XtDestroyWidget instead of XDestroyWindow. (handle_motion_event): Only call handle_single_motion_event once. * xlwmenuP.h (window_state): Add pixmap and w.
author Jan D <jan.h.d@swipnet.se>
date Sat, 17 Apr 2010 19:49:18 +0200
parents 1da056389889 (current diff) 9a798edc503b (diff)
children 32fc68b9d103
files
diffstat 3 files changed, 149 insertions(+), 82 deletions(-) [+]
line wrap: on
line diff
--- a/lwlib/ChangeLog	Sat Apr 17 11:53:55 2010 -0400
+++ b/lwlib/ChangeLog	Sat Apr 17 19:49:18 2010 +0200
@@ -1,3 +1,32 @@
+2010-04-17  Jan Djärv  <jan.h.d@swipnet.se>
+
+	* xlwmenu.c: Include Shell.h, remove duplicate declaration of
+	XlwMenuRedisplay.
+	(display_menu_item): Replace ws->window with ws->pixmap, remove
+	call to XftDrawRect.
+	(display_menu): Remove this and that argument.  Remove
+	just_compute_this_one_p. Fill pixmap at start and copy it to window
+	at end.
+	(expose_cb): New function.
+	(make_windows_if_needed): Replace XCreateWindow with XtCreatePopup.
+	Add eventhandler for expose to expose_cb.  Remove creation of
+	xft_draw.
+	(create_pixmap_for_menu): New function.
+	(remap_menubar): Pop down menus that aren't the same as in old_stack.
+	Set width, heigh, x, y on widget with XtVaSetValues.  Call
+	create_pixmap_for_menu.
+	Replace XUnmapWindow with XtPopdown.
+	Remowe two last parameters to display_menu.
+	(map_event_to_widget_value, XlwMenuRedisplay, Key, Select)
+	(pop_up_menu): Remowe two last parameters to display_menu.
+	(XlwMenuRealize): Call create_pixmap_for_menu, set w and pixmap.
+	Remove call to XftDrawCreate.
+	(XlwMenuDestroy): Free pixmap.  Call XtDestroyWidget instead of
+	XDestroyWindow.
+	(handle_motion_event): Only call handle_single_motion_event once.
+
+	* xlwmenuP.h (window_state): Add pixmap and w.
+
 2010-04-16  YAMAMOTO Mitsuharu  <mituharu@math.s.chiba-u.ac.jp>
 
 	* xlwmenu.c (facename_changed): Put function in #ifdef HAVE_XFT.
--- a/lwlib/xlwmenu.c	Sat Apr 17 11:53:55 2010 -0400
+++ b/lwlib/xlwmenu.c	Sat Apr 17 19:49:18 2010 +0200
@@ -44,6 +44,7 @@
 #include <X11/ObjectP.h>
 #include <X11/StringDefs.h>
 #include <X11/cursorfont.h>
+#include <X11/Shell.h>
 #include "xlwmenuP.h"
 
 #ifdef emacs
@@ -186,7 +187,6 @@
 
 static Boolean XlwMenuSetValues();
 static void XlwMenuRealize();
-static void XlwMenuRedisplay();
 static void XlwMenuResize();
 static void XlwMenuInitialize();
 static void XlwMenuRedisplay();
@@ -1067,13 +1067,13 @@
 
       if (separator_p)
 	{
-	  draw_separator (mw, ws->window, x, y, width, separator);
+	  draw_separator (mw, ws->pixmap, x, y, width, separator);
 	}
       else
 	{
 	  int x_offset = x + h_spacing + shadow;
 	  char* display_string = resource_widget_value (mw, val);
-	  draw_shadow_rectangle (mw, ws->window, x, y, width, height, True,
+	  draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, True,
 				 False);
 
 	  /* Deal with centering a menu title. */
@@ -1092,9 +1092,6 @@
           if (ws->xft_draw)
             {
               int draw_y = y + v_spacing + shadow;
-              XftDrawRect (ws->xft_draw, &mw->menu.xft_bg,
-                           x_offset, draw_y,
-                           ws->width, font_height);
               XftDrawStringUtf8 (ws->xft_draw, xftfg,
                                  mw->menu.xft_font,
                                  x_offset, draw_y + font_ascent,
@@ -1105,13 +1102,13 @@
 #endif
 #ifdef HAVE_X_I18N
           if (mw->menu.fontSet)
-            XmbDrawString (XtDisplay (mw), ws->window, mw->menu.fontSet,
+            XmbDrawString (XtDisplay (mw), ws->pixmap, mw->menu.fontSet,
                            text_gc, x_offset,
                            y + v_spacing + shadow + font_ascent,
                            display_string, strlen (display_string));
           else
 #endif
-          XDrawString (XtDisplay (mw), ws->window,
+          XDrawString (XtDisplay (mw), ws->pixmap,
 		       text_gc, x_offset,
 		       y + v_spacing + shadow + font_ascent,
 		       display_string, strlen (display_string));
@@ -1119,16 +1116,16 @@
 	  if (!horizontal_p)
 	    {
 	      if (val->button_type == BUTTON_TYPE_TOGGLE)
-		draw_toggle (mw, ws->window, x, y + v_spacing + shadow,
+		draw_toggle (mw, ws->pixmap, x, y + v_spacing + shadow,
 			     val->selected);
 	      else if (val->button_type == BUTTON_TYPE_RADIO)
-		draw_radio (mw, ws->window, x, y + v_spacing + shadow,
+		draw_radio (mw, ws->pixmap, x, y + v_spacing + shadow,
 			    val->selected);
 
 	      if (val->contents)
 		{
 		  int a_w = arrow_width (mw);
-		  draw_arrow (mw, ws->window, deco_gc,
+		  draw_arrow (mw, ws->pixmap, deco_gc,
 			      x + width - a_w
 			      - mw->menu.horizontal_spacing
 			      - mw->menu.shadow_thickness,
@@ -1154,7 +1151,7 @@
 #endif
 #ifdef HAVE_X_I18N
                   if (mw->menu.fontSet)
-                    XmbDrawString (XtDisplay (mw), ws->window,
+                    XmbDrawString (XtDisplay (mw), ws->pixmap,
                                    mw->menu.fontSet,
                                    text_gc,
                                    x + label_width + mw->menu.arrow_spacing,
@@ -1162,7 +1159,7 @@
                                    val->key, strlen (val->key));
                   else
 #endif
-		  XDrawString (XtDisplay (mw), ws->window,
+		  XDrawString (XtDisplay (mw), ws->pixmap,
 			       text_gc,
 			       x + label_width + mw->menu.arrow_spacing,
 			       y + v_spacing + shadow + font_ascent,
@@ -1171,17 +1168,17 @@
 	    }
 	  else
 	    {
-	      XDrawRectangle (XtDisplay (mw), ws->window,
+	      XDrawRectangle (XtDisplay (mw), ws->pixmap,
 			      mw->menu.background_gc,
 			      x + shadow, y + shadow,
 			      label_width + h_spacing - 1,
 			      font_height + 2 * v_spacing - 1);
-	      draw_shadow_rectangle (mw, ws->window, x, y, width, height,
+	      draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height,
 				     True, False);
 	    }
 
 	  if (highlighted_p)
-	    draw_shadow_rectangle (mw, ws->window, x, y, width, height, False,
+	    draw_shadow_rectangle (mw, ws->pixmap, x, y, width, height, False,
 				   False);
 	}
     }
@@ -1191,16 +1188,13 @@
 }
 
 static void
-display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return,
-	      this, that)
+display_menu (mw, level, just_compute_p, highlighted_pos, hit, hit_return)
      XlwMenuWidget mw;
      int level;
      Boolean just_compute_p;
      XPoint* highlighted_pos;
      XPoint* hit;
      widget_value** hit_return;
-     widget_value* this;
-     widget_value* that;
 {
   widget_value*	val;
   widget_value* following_item;
@@ -1208,9 +1202,6 @@
   XPoint	where;
   int horizontal_p = mw->menu.horizontal && (level == 0);
   int highlighted_p;
-  int just_compute_this_one_p;
-  /* This is set nonzero if the element containing HIGHLIGHTED_POS
-     is disabled, so that we do not return any subsequent element either.  */
   int no_return = 0;
   enum menu_separator separator;
 
@@ -1229,6 +1220,11 @@
   where.y = 0;
 
   ws = &mw->menu.windows [level];
+
+  if (!just_compute_p)
+    XFillRectangle (XtDisplay (mw), ws->pixmap, mw->menu.background_gc,
+                    0, 0, ws->width, ws->height);
+
   for (val = mw->menu.old_stack [level]->contents; val; val = val->next)
     {
       highlighted_p = val == following_item;
@@ -1240,11 +1236,8 @@
 	    highlighted_pos->y = where.y;
 	}
 
-      just_compute_this_one_p =
-	just_compute_p || ((this || that) && val != this &&  val != that);
-
       display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p,
-			 just_compute_this_one_p);
+			 just_compute_p);
 
       if (highlighted_p && highlighted_pos)
 	{
@@ -1282,8 +1275,12 @@
     }
 
   if (!just_compute_p)
-    draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height,
-			   False, False);
+    {
+      draw_shadow_rectangle (mw, ws->pixmap, 0, 0, ws->width, ws->height,
+                             False, False);
+      XCopyArea (XtDisplay (mw), ws->pixmap, ws->window,
+                 mw->menu.foreground_gc, 0, 0, ws->width, ws->height, 0, 0);
+    }
 }
 
 /* Motion code */
@@ -1302,15 +1299,28 @@
 }
 
 static void
+expose_cb (Widget widget,
+           XtPointer closure,
+           XEvent* event,
+           Boolean* continue_to_dispatch)
+{
+  *continue_to_dispatch = False;
+  XlwMenuWidget mw = (XlwMenuWidget) closure;
+  int i;
+
+  for (i = 0; i < mw->menu.windows_length; ++i)
+    if (mw->menu.windows [i].w == widget) break;
+  if (i < mw->menu.windows_length && i < mw->menu.old_depth)
+    display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
+}
+
+static void
 make_windows_if_needed (mw, n)
      XlwMenuWidget mw;
      int n;
 {
   int i;
   int start_at;
-  XSetWindowAttributes xswa;
-  int mask;
-  Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw)));
   window_state* windows;
 #ifdef HAVE_XFT
   int screen = XScreenNumberOfScreen (mw->core.screen);
@@ -1319,17 +1329,6 @@
   if (mw->menu.windows_length >= n)
     return;
 
-  xswa.save_under = True;
-  xswa.override_redirect = True;
-  xswa.background_pixel = mw->core.background_pixel;
-  xswa.border_pixel = mw->core.border_pixel;
-  xswa.event_mask =
-    ExposureMask | PointerMotionMask | PointerMotionHintMask
-      | ButtonReleaseMask | ButtonPressMask;
-  xswa.cursor = mw->menu.cursor_shape;
-  mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel
-    | CWEventMask | CWCursor;
-
   if (!mw->menu.windows)
     {
       mw->menu.windows =
@@ -1349,23 +1348,29 @@
 
   for (i = start_at; i < n; i++)
    {
+     Arg av[10];
+     int ac = 0;
      windows [i].x = 0;
      windows [i].y = 0;
      windows [i].width = 1;
      windows [i].height = 1;
      windows [i].max_rest_width = 0;
-     windows [i].window =
-       XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1,
-		      0, 0, CopyFromParent, CopyFromParent, mask, &xswa);
+     XtSetArg (av[ac], XtNwidth, 1); ++ac;
+     XtSetArg (av[ac], XtNheight, 1); ++ac;
+     XtSetArg (av[ac], XtNsaveUnder, True); ++ac;
+     XtSetArg (av[ac], XtNbackground, mw->core.background_pixel); ++ac;
+     XtSetArg (av[ac], XtNborderColor, mw->core.border_pixel); ++ac;
+     XtSetArg (av[ac], XtNborderWidth, mw->core.border_width); ++ac;
+     XtSetArg (av[ac], XtNcursor, mw->menu.cursor_shape); ++ac;
+     windows [i].w =
+       XtCreatePopupShell ("sub", overrideShellWidgetClass,
+                           (Widget) mw, av, ac);
+     XtRealizeWidget (windows [i].w);
+     XtAddEventHandler (windows [i].w, ExposureMask, False, expose_cb, mw);
+     windows [i].window = XtWindow (windows [i].w);
+     windows [i].pixmap = None;
 #ifdef HAVE_XFT
-     if (mw->menu.xft_font)
-       mw->menu.windows [i].xft_draw
-         = XftDrawCreate (XtDisplay (mw),
-                          windows [i].window,
-                          DefaultVisual (XtDisplay (mw), screen),
-                          mw->core.colormap);
-     else
-       mw->menu.windows [i].xft_draw = 0;
+     windows [i].xft_draw = 0;
 #endif
    }
 }
@@ -1445,6 +1450,33 @@
     }
 }
 
+static void
+create_pixmap_for_menu (window_state* ws, XlwMenuWidget mw)
+{
+  if (ws->pixmap != None) 
+    {
+      XFreePixmap (XtDisplay (ws->w), ws->pixmap);
+      ws->pixmap = None;
+    }
+  ws->pixmap = XCreatePixmap (XtDisplay (ws->w), ws->window,
+                              ws->width, ws->height,
+                              DefaultDepthOfScreen (XtScreen (ws->w)));
+#ifdef HAVE_XFT
+  if (ws->xft_draw)
+    XftDrawDestroy (ws->xft_draw);
+  if (mw->menu.xft_font)
+    {
+      int screen = XScreenNumberOfScreen (mw->core.screen);
+      ws->xft_draw = XftDrawCreate (XtDisplay (ws->w),
+                                    ws->pixmap,
+                                    DefaultVisual (XtDisplay (ws->w), screen),
+                                    mw->core.colormap);
+    }
+  else
+    ws->xft_draw = 0;
+#endif
+}
+
 /* Updates old_stack from new_stack and redisplays. */
 static void
 remap_menubar (mw)
@@ -1490,14 +1522,17 @@
   /* updates old_state from new_state.  It has to be done now because
      display_menu (called below) uses the old_stack to know what to display. */
   for (i = last_same + 1; i < new_depth; i++)
-    old_stack [i] = new_stack [i];
+    {
+      XtPopdown (mw->menu.windows [i].w);
+      old_stack [i] = new_stack [i];
+    }
   mw->menu.old_depth = new_depth;
 
   /* refresh the last selection */
   selection_position.x = 0;
   selection_position.y = 0;
   display_menu (mw, last_same, new_selection == old_selection,
-		&selection_position, NULL, NULL, old_selection, new_selection);
+		&selection_position, NULL, NULL);
 
   /* Now place the new menus.  */
   for (i = last_same + 1; i < new_depth && new_stack[i]->contents; i++)
@@ -1524,17 +1559,17 @@
 
       fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1);
 
-      XClearWindow (XtDisplay (mw), ws->window);
-      XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y,
-			 ws->width, ws->height);
-      XMapRaised (XtDisplay (mw), ws->window);
-      display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL);
+      XtVaSetValues (ws->w, XtNwidth, ws->width, XtNheight, ws->height,
+                     XtNx, ws->x, XtNy, ws->y, NULL);
+      create_pixmap_for_menu (ws, mw);
+      XtPopup (ws->w, XtGrabNone);
+      display_menu (mw, i, False, &selection_position, NULL, NULL);
     }
 
   /* unmap the menus that popped down */
   for (i = new_depth - 1; i < old_depth; i++)
     if (i >= new_depth || (i > 0 && !new_stack[i]->contents))
-      XUnmapWindow (XtDisplay (mw), windows[i].window);
+      XtPopdown (windows[i].w);
 }
 
 static Boolean
@@ -1575,7 +1610,7 @@
       if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos))
 	{
           inside = 1;
-	  display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL);
+	  display_menu (mw, i, True, NULL, &relative_pos, val);
 
 	  if (*val)
 	    {
@@ -1954,6 +1989,7 @@
   mw->menu.windows [0].width = 0;
   mw->menu.windows [0].height = 0;
   mw->menu.windows [0].max_rest_width = 0;
+  mw->menu.windows [0].pixmap = None;
 #ifdef HAVE_XFT
   mw->menu.windows [0].xft_draw = 0;
 #endif
@@ -1996,22 +2032,19 @@
   x_uncatch_errors ();
 #endif
 
+  mw->menu.windows [0].w = w;
   mw->menu.windows [0].window = XtWindow (w);
   mw->menu.windows [0].x = w->core.x;
   mw->menu.windows [0].y = w->core.y;
   mw->menu.windows [0].width = w->core.width;
   mw->menu.windows [0].height = w->core.height;
 
+  create_pixmap_for_menu (&mw->menu.windows [0], mw);
+
 #ifdef HAVE_XFT
   if (mw->menu.xft_font)
     {
       XColor colors[3];
-      int screen = XScreenNumberOfScreen (mw->core.screen);
-      mw->menu.windows [0].xft_draw
-        = XftDrawCreate (XtDisplay (w),
-                         mw->menu.windows [0].window,
-                         DefaultVisual (XtDisplay (w), screen),
-                         mw->core.colormap);
       colors[0].pixel = mw->menu.xft_fg.pixel = mw->menu.foreground;
       colors[1].pixel = mw->menu.xft_bg.pixel = mw->core.background_pixel;
       colors[2].pixel = mw->menu.xft_disabled_fg.pixel
@@ -2030,8 +2063,6 @@
       mw->menu.xft_disabled_fg.color.green = colors[2].green;
       mw->menu.xft_disabled_fg.color.blue = colors[2].blue;
     }
-  else
-    mw->menu.windows [0].xft_draw = 0;
 #endif
 }
 
@@ -2055,8 +2086,7 @@
       submenu_destroyed = 0;
     }
 
-  for (i = 0; i < mw->menu.old_depth; i++)
-    display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL);
+  display_menu (mw, 0, False, NULL, NULL, NULL);
 }
 
 
@@ -2121,10 +2151,14 @@
     XftFontClose (XtDisplay (mw), mw->menu.xft_font);
 #endif
 
+  if (mw->menu.windows [0].pixmap != None) 
+    XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap);
   /* start from 1 because the one in slot 0 is w->core.window */
   for (i = 1; i < mw->menu.windows_length; i++)
     {
-      XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window);
+      if (mw->menu.windows [i].pixmap != None) 
+        XFreePixmap (XtDisplay (mw), mw->menu.windows [i].pixmap);
+      XtDestroyWidget (mw->menu.windows [i].w);
 #ifdef HAVE_XFT
       if (mw->menu.windows [i].xft_draw)
         XftDrawDestroy (mw->menu.windows [i].xft_draw);
@@ -2140,7 +2174,7 @@
 facename_changed (XlwMenuWidget newmw,
                   XlwMenuWidget oldmw)
 {
-  /* This will fore a new XftFont even if the same sting is set.
+  /* This will fore a new XftFont even if the same string is set.
      This is good, as rendering parameters may have changed and
      we just want to do a redisplay.  */
   return newmw->menu.faceName != oldmw->menu.faceName;
@@ -2250,13 +2284,14 @@
       /* Don't allow the popup menu to resize itself.  */
       mw->core.width = mw->menu.windows [0].width;
       mw->core.height = mw->menu.windows [0].height;
-      mw->core.parent->core.width = mw->core.width ;
-      mw->core.parent->core.height = mw->core.height ;
+      mw->core.parent->core.width = mw->core.width;
+      mw->core.parent->core.height = mw->core.height;
     }
   else
     {
       mw->menu.windows [0].width = mw->core.width;
       mw->menu.windows [0].height = mw->core.height;
+      create_pixmap_for_menu (&mw->menu.windows [0], mw);
     }
 }
 
@@ -2287,8 +2322,7 @@
   int x = ev->x_root;
   int y = ev->y_root;
   int state = ev->state;
-
-  handle_single_motion_event (mw, ev);
+  XMotionEvent oldev = *ev;
 
   /* allow motion events to be generated again */
   if (ev->is_hint
@@ -2300,6 +2334,8 @@
       && ev->state == state
       && (ev->x_root != x || ev->y_root != y))
     handle_single_motion_event (mw, ev);
+  else
+    handle_single_motion_event (mw, &oldev);
 }
 
 static void
@@ -2609,7 +2645,7 @@
       else
 	{
 	  XtRemoveGrab ((Widget) mw);
-	  display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
+	  display_menu (mw, 0, False, NULL, NULL, NULL);
 	}
     }
 
@@ -2650,7 +2686,7 @@
       else
 	{
 	  XtRemoveGrab ((Widget) mw);
-	  display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
+	  display_menu (mw, 0, False, NULL, NULL, NULL);
 	}
     }
 
@@ -2701,7 +2737,7 @@
       XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h,
 			 XtParent ((Widget)mw)->core.border_width);
       XtPopup (XtParent ((Widget)mw), XtGrabExclusive);
-      display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL);
+      display_menu (mw, 0, False, NULL, NULL, NULL);
       mw->menu.windows [0].x = x + borderwidth;
       mw->menu.windows [0].y = y + borderwidth;
       mw->menu.top_depth = 1;  /* Popup menus don't have a bar so top is 1  */
--- a/lwlib/xlwmenuP.h	Sat Apr 17 11:53:55 2010 -0400
+++ b/lwlib/xlwmenuP.h	Sat Apr 17 19:49:18 2010 +0200
@@ -32,7 +32,9 @@
 /* Elements in the stack arrays. */
 typedef struct _window_state
 {
+  Widget        w;
   Window	window;
+  Pixmap        pixmap;
   Position	x;
   Position	y;
   Dimension	width;