Hey Chris
Comments below.
>
> I'm also not sure flushing the paint queue before doing the XCopyArea
> (suggestion 1 above) will be enough either. It's very easy to imagine
> a scenario where you're scrolling and we do another ScrollWindow
> before all the GraphicsExpose events have been received. We really do
> need to block things somehow. And it'll be more than just blocking
> the message queue - we'll need to actually stop the thread if there's
> a call to ScrollWindow (or anything else that ends up copying) until
> the GraphicsExposes have been handled.
I went and began to review our code and wondering how to do it the
proper way - then went to Gtk+ scrolling code once more, and then could
see that the gtk textbox was handling the scroll itself (it seems the
algorithm is what's called 'smooth scrolling', not sure).
The idea is that after each request to scroll, they call XIfEvent to
wait for GraphicsExpose/NoExpose events on the scrolled window, blocking
the thread until they get one of them (so you can invalidate or exit the
loop), and adding a expose operation on the window.
I implemented this today for our implementation and it seems to work
just fine, and it seems it follows your idea ;-)
Take a look and tell what you think.
Carlos.
PD - The patch is a test, so needs to be beautified still ;-) But should
work.
Index: XplatUIX11.cs
===================================================================
--- XplatUIX11.cs (revisión: 109846)
+++ XplatUIX11.cs (copia de trabajo)
@@ -4860,6 +4860,33 @@
y = dest_y_return;
}
+ bool GraphicsExposePredicate (IntPtr display, ref XEvent xevent, IntPtr arg)
+ {
+ return (xevent.type == XEventName.GraphicsExpose || xevent.type == XEventName.NoExpose) &&
+ arg == xevent.GraphicsExposeEvent.drawable;
+ }
+
+ delegate bool EventPredicate (IntPtr display, ref XEvent xevent, IntPtr arg);
+
+ void ProcessGraphicsExpose (Hwnd hwnd)
+ {
+ XEvent xevent = new XEvent ();
+ IntPtr handle = Hwnd.HandleFromObject (hwnd);
+ EventPredicate predicate = GraphicsExposePredicate;
+
+ for (;;) {
+ XIfEvent (Display, ref xevent, predicate, handle);
+ if (xevent.type != XEventName.GraphicsExpose)
+ break;
+
+ AddExpose (hwnd, xevent.ExposeEvent.window == hwnd.ClientWindow, xevent.GraphicsExposeEvent.x, xevent.GraphicsExposeEvent.y,
+ xevent.GraphicsExposeEvent.width, xevent.GraphicsExposeEvent.height);
+
+ if (xevent.GraphicsExposeEvent.count == 0)
+ break;
+ }
+ }
+
internal override void ScrollWindow(IntPtr handle, Rectangle area, int XAmount, int YAmount, bool with_children) {
Hwnd hwnd;
IntPtr gc;
@@ -4913,6 +4940,8 @@
Rectangle dirty_area = GetDirtyArea (area, dest_rect, XAmount, YAmount);
AddExpose (hwnd, true, dirty_area.X, dirty_area.Y, dirty_area.Width, dirty_area.Height);
+ ProcessGraphicsExpose (hwnd);
+
XFreeGC(DisplayHandle, gc);
}
@@ -4966,94 +4995,10 @@
visible_area.Intersect (r);
}
- // If region is null, the entire area is visible.
- // Get the area not obscured otherwise.
- Region visible_region = GetVisibleRegion (c, visible_area);
- if (visible_region != null) {
- RectangleF rectf = visible_region.GetBounds (Hwnd.GraphicsContext);
- visible_area = new Rectangle ((int) rectf.X, (int) rectf.Y,
- (int) rectf.Width, (int) rectf.Height);
-
- visible_region.Dispose ();
- }
-
visible_area.Location = c.PointToClient (visible_area.Location);
return visible_area;
}
- // Obscured area by other toplevel windows
- Region GetVisibleRegion (Control c, Rectangle visible_area)
- {
- Region visible_region = null;
- IntPtr Root;
- IntPtr Parent;
- IntPtr Children;
- int ChildCount;
-
- Control form = c.FindForm ();
- if (form == null || !form.IsHandleCreated)
- return null;
-
- Hwnd hwnd = Hwnd.GetObjectFromWindow (form.Handle);
- IntPtr form_handle = hwnd.whole_window;
-
- lock (XlibLock) {
- XQueryTree (DisplayHandle, RootWindow, out Root, out Parent, out Children, out ChildCount);
- }
-
- int intptr_size = IntPtr.Size;
- bool above = false;
-
- for (int i = 0; i < ChildCount; i++) {
- IntPtr window = Marshal.ReadIntPtr (Children, i * intptr_size);
-
- XWindowAttributes win_attrs = new XWindowAttributes ();
- lock (XlibLock) {
- XGetWindowAttributes (DisplayHandle, window, ref win_attrs);
- }
-
- Rectangle win_area = new Rectangle (win_attrs.x, win_attrs.y,
- win_attrs.width, win_attrs.height);
-
- if (win_attrs.map_state != MapState.IsViewable || !win_area.IntersectsWith (visible_area))
- continue;
-
- IntPtr SubChildren;
- int SubChildCount;
-
- if (above) {
- if (visible_region == null)
- visible_region = new Region (visible_area);
-
- visible_region.Exclude (win_area);
- continue;
- }
-
- lock (XlibLock) {
- XQueryTree (DisplayHandle, window, out Root, out Parent, out SubChildren, out SubChildCount);
- }
-
- if (SubChildren != IntPtr.Zero) {
- IntPtr sub_win = new IntPtr (Marshal.ReadInt32 (SubChildren));
-
- lock (XlibLock) {
- XFree (SubChildren);
- }
-
- if (sub_win == form_handle)
- above = true;
- }
- }
-
- if (Children != IntPtr.Zero) {
- lock (XlibLock) {
- XFree (Children);
- }
- }
-
- return visible_region;
- }
-
internal override void SendAsyncMethod (AsyncMethodData method) {
Hwnd hwnd;
XEvent xevent = new XEvent ();
@@ -6286,6 +6231,9 @@
[DllImport ("libX11")]
internal extern static void XPeekEvent (IntPtr display, ref XEvent xevent);
+
+ [DllImport ("libX11")]
+ internal extern static void XIfEvent (IntPtr display, ref XEvent xevent, Delegate event_predicate, IntPtr arg);
#endregion
}
}
_______________________________________________
Mono-winforms-list maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-winforms-list