// Message Loop
private static Hashtable MessageQueues; // Holds our thread-specific XEventQueues
+ private static ArrayList unattached_timer_list; // holds timers that are enabled but not attached to a window.
#if __MonoCS__ //
private static Pollfd[] pollfds; // For watching the X11 socket
private static bool wake_waiting;
// Caret
private static CaretStruct Caret; //
+ // Last window containing the pointer
+ private static IntPtr LastPointerWindow; // The last window containing the pointer
+
// Our atoms
private static IntPtr WM_PROTOCOLS;
private static IntPtr WM_DELETE_WINDOW;
// Now regular initialization
XlibLock = new object ();
+ X11Keyboard.XlibLock = XlibLock;
MessageQueues = Hashtable.Synchronized (new Hashtable(7));
+ unattached_timer_list = ArrayList.Synchronized (new ArrayList (3));
XInitThreads();
ErrorExceptions = false;
is ignored by metacity. */
functions |= MotifFunctions.Move | MotifFunctions.Resize | MotifFunctions.Minimize | MotifFunctions.Maximize;
} else if (form != null && form.FormBorderStyle == FormBorderStyle.None) {
- // No functions nor decorations whatsoever.
+ functions |= MotifFunctions.All;
} else {
if (StyleSet (cp.Style, WindowStyles.WS_CAPTION)) {
functions |= MotifFunctions.Move;
}
private int NextTimeout (ArrayList timers, DateTime now) {
- int timeout = Int32.MaxValue;
+ int timeout = 0;
foreach (Timer timer in timers) {
int next = (int) (timer.Expires - now).TotalMilliseconds;
timer = (Timer) timers [i];
- if (timer.Enabled && timer.Expires <= now) {
+ if (timer.Enabled && timer.Expires <= now && !timer.Busy) {
+ timer.Busy = true;
timer.Update (now);
timer.FireTick ();
+ timer.Busy = false;
}
}
}
private void MapWindow(Hwnd hwnd, WindowType windows) {
if (!hwnd.mapped) {
- if (Control.FromHandle(hwnd.Handle) is Form) {
- Form f = Control.FromHandle(hwnd.Handle) as Form;
- if (f.WindowState == FormWindowState.Normal)
+ Form f = Control.FromHandle(hwnd.Handle) as Form;
+ if (f != null) {
+ if (f.WindowState == FormWindowState.Normal) {
+ f.waiting_showwindow = true;
SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero);
+ }
}
// it's possible that our Hwnd is no
if (hwnd.zombie)
return;
- bool need_to_wait = false;
-
if ((windows & WindowType.Whole) != 0) {
XMapWindow(DisplayHandle, hwnd.whole_window);
}
if ((windows & WindowType.Client) != 0) {
XMapWindow(DisplayHandle, hwnd.client_window);
-
- need_to_wait = true;
}
hwnd.mapped = true;
- if (need_to_wait && Control.FromHandle(hwnd.Handle) is Form)
+ if (f != null && f.waiting_showwindow)
WaitForHwndMessage (hwnd, Msg.WM_SHOWWINDOW);
}
}
private void UnmapWindow(Hwnd hwnd, WindowType windows) {
if (hwnd.mapped) {
+ Form f = null;
if (Control.FromHandle(hwnd.Handle) is Form) {
- Form f = Control.FromHandle(hwnd.Handle) as Form;
- if (f.WindowState == FormWindowState.Normal)
+ f = Control.FromHandle(hwnd.Handle) as Form;
+ if (f.WindowState == FormWindowState.Normal) {
+ f.waiting_showwindow = true;
SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, IntPtr.Zero, IntPtr.Zero);
+ }
}
// it's possible that our Hwnd is no
// longer valid after making that
// SendMessage call, so check here.
+ // FIXME: it is likely wrong, as it has already sent WM_SHOWWINDOW
if (hwnd.zombie)
return;
- bool need_to_wait = false;
-
if ((windows & WindowType.Client) != 0) {
XUnmapWindow(DisplayHandle, hwnd.client_window);
-
- need_to_wait = true;
}
if ((windows & WindowType.Whole) != 0) {
XUnmapWindow(DisplayHandle, hwnd.whole_window);
hwnd.mapped = false;
- if (need_to_wait && Control.FromHandle(hwnd.Handle) is Form)
+ if (f != null && f.waiting_showwindow)
WaitForHwndMessage (hwnd, Msg.WM_SHOWWINDOW);
}
}
xevent.AnyEvent.type == XEventName.KeyRelease) {
// PreFilter() handles "shift key state updates.
Keyboard.PreFilter (xevent);
- if (XFilterEvent(ref xevent, FosterParent)) {
+ if (XFilterEvent (ref xevent, Keyboard.ClientWindow)) {
// probably here we could raise WM_IME_KEYDOWN and
// WM_IME_KEYUP, but I'm not sure it is worthy.
continue;
// Modality handling, if we are modal and the new active window is one
// of ours but not the modal one, switch back to the modal window
- if (NativeWindow.FindWindow(ActiveWindow) != null) {
+ if (NativeWindow.FromHandle(ActiveWindow) != null) {
if (ActiveWindow != (IntPtr)ModalWindows.Peek()) {
Activate((IntPtr)ModalWindows.Peek());
}
else if (xevent.PropertyEvent.atom == _NET_WM_STATE) {
// invalidate our cache - we'll query again the next time someone does GetWindowState.
hwnd.cached_window_state = (FormWindowState)(-1);
+ PostMessage (hwnd.Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
}
break;
MoveResizeWindow (DisplayHandle, hwnd.client_window, rect.X, rect.Y, rect.Width, rect.Height);
}
- AddExpose (hwnd, false, 0, 0, hwnd.Width, hwnd.Height);
+ AddExpose (hwnd, hwnd.WholeWindow == hwnd.ClientWindow, 0, 0, hwnd.Width, hwnd.Height);
}
#endregion // Private Methods
}
}
- internal override Size MinimizedWindowSize {
- get {
- return new Size(1, 1);
- }
- }
-
internal override Size MinimizedWindowSpacingSize {
get {
return new Size(1, 1);
get { return new Size (2, 2); }
}
- internal override Size MinWindowTrackSize {
- get {
- return new Size(1, 1);
- }
- }
-
internal override Keys ModifierKeys {
get {
return Keyboard.ModifierKeys;
}
}
+ internal override MouseButtons MouseButtons {
+ get {
+ return MouseState;
+ }
+ }
+
internal override Rectangle VirtualScreen {
get {
IntPtr actual_atom;
lock (XlibLock) {
if (true /* the window manager supports NET_ACTIVE_WINDOW */) {
SendNetWMMessage(hwnd.whole_window, _NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero);
+ XEventQueue q = null;
+ lock (unattached_timer_list) {
+ foreach (Timer t in unattached_timer_list) {
+ if (q == null)
+ q= (XEventQueue) MessageQueues [Thread.CurrentThread];
+ t.thread = q.Thread;
+ q.timer_list.Add (t);
+ }
+ unattached_timer_list.Clear ();
+ }
}
// else {
// XRaiseWindow(DisplayHandle, handle);
#if DriverDebug || DriverDebugDestroy
Console.WriteLine ("XDestroyWindow (whole_window = {0:X})", hwnd.whole_window.ToInt32());
#endif
+ Keyboard.DestroyICForWindow (hwnd.whole_window);
XDestroyWindow(DisplayHandle, hwnd.whole_window);
}
else if (hwnd.client_window != IntPtr.Zero) {
#if DriverDebug || DriverDebugDestroy
Console.WriteLine ("XDestroyWindow (client_window = {0:X})", hwnd.client_window.ToInt32());
#endif
+ Keyboard.DestroyICForWindow (hwnd.client_window);
XDestroyWindow(DisplayHandle, hwnd.client_window);
}
// hwnds, since much of the event handling code makes requests using the hwnd's
// client_window, and that'll result in BadWindow errors if there's some lag
// between the XDestroyWindow call and the DestroyNotify event.
- if (hwnd == null || hwnd.zombie) {
+ if (hwnd == null || hwnd.zombie && xevent.AnyEvent.type != XEventName.ClientMessage) {
#if DriverDebug || DriverDebugDestroy
Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}", xevent.type, xevent.AnyEvent.window.ToInt32());
#endif
msg.hwnd = hwnd.Handle;
+ // Windows sends WM_ENTERSIZEMOVE when a form resize/move operation starts and WM_EXITSIZEMOVE
+ // when it is done. The problem in X11 is that there is no concept of start-end of a moving/sizing.
+ // Configure events ("this window has resized/moved") are sent for each step of the resize. We send a
+ // WM_ENTERSIZEMOVE when we get the first Configure event. The problem is the WM_EXITSIZEMOVE.
+ //
+ // - There is no way for us to know which is the last Configure event. We can't traverse the events
+ // queue, because the next configure event might not be pending yet.
+ // - We can't get ButtonPress/Release events for the window decorations, because they are not part
+ // of the window(s) we manage.
+ // - We can't rely on the mouse state to change to "up" before the last Configure event. It doesn't.
+ //
+ // We are almost 100% guaranteed to get another event (e.g Expose or other), but we can't know for sure
+ // which, so we have here to check if the mouse buttons state is "up" and send the WM_EXITSIZEMOVE
+ //
+ if (hwnd.resizing_or_moving) {
+ int root_x, root_y, win_x, win_y, keys_buttons;
+ IntPtr root, child;
+ XQueryPointer (DisplayHandle, hwnd.Handle, out root, out child, out root_x, out root_y,
+ out win_x, out win_y, out keys_buttons);
+ if ((keys_buttons & (int)MouseKeyMasks.Button1Mask) == 0 &&
+ (keys_buttons & (int)MouseKeyMasks.Button2Mask) == 0 &&
+ (keys_buttons & (int)MouseKeyMasks.Button3Mask) == 0) {
+ hwnd.resizing_or_moving = false;
+ SendMessage (hwnd.Handle, Msg.WM_EXITSIZEMOVE, IntPtr.Zero, IntPtr.Zero);
+ }
+ }
+
//
// If you add a new event to this switch make sure to add it in
// UpdateMessage also unless it is not coming through the X event system.
MouseState |= MouseButtons.Left;
if (client) {
msg.message = Msg.WM_LBUTTONDOWN;
+ msg.wParam = GetMousewParam (0);
} else {
msg.message = Msg.WM_NCLBUTTONDOWN;
+ msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
- // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down
- msg.wParam=GetMousewParam(0);
break;
}
MouseState |= MouseButtons.Middle;
if (client) {
msg.message = Msg.WM_MBUTTONDOWN;
+ msg.wParam = GetMousewParam (0);
} else {
msg.message = Msg.WM_NCMBUTTONDOWN;
+ msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
- msg.wParam=GetMousewParam(0);
break;
}
MouseState |= MouseButtons.Right;
if (client) {
msg.message = Msg.WM_RBUTTONDOWN;
+ msg.wParam = GetMousewParam (0);
} else {
msg.message = Msg.WM_NCRBUTTONDOWN;
+ msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
- msg.wParam=GetMousewParam(0);
break;
}
if (msg.message == Msg.WM_LBUTTONDOWN || msg.message == Msg.WM_MBUTTONDOWN || msg.message == Msg.WM_RBUTTONDOWN) {
SendParentNotify(msg.hwnd, msg.message, mouse_position.X, mouse_position.Y);
-
- // Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or
- // not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after
- // mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh*
- XEvent motionEvent = new XEvent ();
- motionEvent.type = XEventName.MotionNotify;
- motionEvent.MotionEvent.display = DisplayHandle;
- motionEvent.MotionEvent.window = xevent.ButtonEvent.window;
- motionEvent.MotionEvent.x = xevent.ButtonEvent.x;
- motionEvent.MotionEvent.y = xevent.ButtonEvent.y;
- hwnd.Queue.EnqueueLocked (motionEvent);
}
break;
}
case XEventName.ButtonRelease: {
-
switch(xevent.ButtonEvent.button) {
case 1: {
if (client) {
msg.message = Msg.WM_LBUTTONUP;
} else {
msg.message = Msg.WM_NCLBUTTONUP;
+ msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
MouseState &= ~MouseButtons.Left;
- msg.wParam=GetMousewParam(0);
+ msg.wParam = GetMousewParam (0);
break;
}
msg.message = Msg.WM_MBUTTONUP;
} else {
msg.message = Msg.WM_NCMBUTTONUP;
+ msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
MouseState &= ~MouseButtons.Middle;
- msg.wParam=GetMousewParam(0);
+ msg.wParam = GetMousewParam (0);
break;
}
msg.message = Msg.WM_RBUTTONUP;
} else {
msg.message = Msg.WM_NCRBUTTONUP;
+ msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y);
}
MouseState &= ~MouseButtons.Right;
- msg.wParam=GetMousewParam(0);
+ msg.wParam = GetMousewParam (0);
break;
}
} else {
HitTest ht;
IntPtr dummy;
- int screen_x;
- int screen_y;
#if DriverDebugExtra
Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}", client ? hwnd.client_window.ToInt32() : hwnd.whole_window.ToInt32(), xevent.MotionEvent.x, xevent.MotionEvent.y);
msg.lParam = (IntPtr)(mouse_position.Y << 16 | mouse_position.X);
}
- // The hit test is sent in screen coordinates
- XTranslateCoordinates (DisplayHandle, xevent.AnyEvent.window, RootWindow,
- xevent.MotionEvent.x, xevent.MotionEvent.y,
- out screen_x, out screen_y, out dummy);
-
- msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF);
- ht = (HitTest)NativeWindow.WndProc (hwnd.client_window, Msg.WM_NCHITTEST,
- IntPtr.Zero, msg.lParam).ToInt32 ();
+ ht = NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y);
NativeWindow.WndProc(hwnd.client_window, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht);
mouse_position.X = xevent.MotionEvent.x;
if (!hwnd.Enabled) {
goto ProcessNextMessage;
}
- if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) {
+ if (xevent.CrossingEvent.mode == NotifyMode.NotifyGrab || xevent.AnyEvent.window != hwnd.client_window) {
goto ProcessNextMessage;
}
+ if (xevent.CrossingEvent.mode == NotifyMode.NotifyUngrab) { // Pseudo motion caused by grabbing
+ if (LastPointerWindow == xevent.AnyEvent.window)
+ goto ProcessNextMessage;
+
+ if (LastPointerWindow != IntPtr.Zero) {
+ Point enter_loc = new Point (xevent.ButtonEvent.x, xevent.ButtonEvent.y);
+
+ // We need this due to EnterNotify being fired on all the parent controls
+ // of the Control being grabbed, and obviously in that scenario we are not
+ // actuallty entering them
+ Control ctrl = Control.FromHandle (hwnd.client_window);
+ foreach (Control child_control in ctrl.Controls)
+ if (child_control.Bounds.Contains (enter_loc))
+ goto ProcessNextMessage;
+
+ // A MouseLeave/LeaveNotify event is sent to the previous window
+ // until the mouse is ungrabbed, not when actually leaving its bounds
+ int x = xevent.CrossingEvent.x_root;
+ int y = xevent.CrossingEvent.y_root;
+ ScreenToClient (LastPointerWindow, ref x, ref y);
+
+ XEvent leaveEvent = new XEvent ();
+ leaveEvent.type = XEventName.LeaveNotify;
+ leaveEvent.CrossingEvent.display = DisplayHandle;
+ leaveEvent.CrossingEvent.window = LastPointerWindow;
+ leaveEvent.CrossingEvent.x = x;
+ leaveEvent.CrossingEvent.y = y;
+ leaveEvent.CrossingEvent.mode = NotifyMode.NotifyNormal;
+ Hwnd last_pointer_hwnd = Hwnd.ObjectFromHandle (LastPointerWindow);
+ last_pointer_hwnd.Queue.EnqueueLocked (leaveEvent);
+ }
+ }
+
+ LastPointerWindow = xevent.AnyEvent.window;
+
msg.message = Msg.WM_MOUSE_ENTER;
HoverState.X = xevent.CrossingEvent.x;
HoverState.Y = xevent.CrossingEvent.y;
HoverState.Timer.Enabled = true;
HoverState.Window = xevent.CrossingEvent.window;
+
+ // Win32 sends a WM_MOUSEMOVE after mouse enter
+ XEvent motionEvent = new XEvent ();
+ motionEvent.type = XEventName.MotionNotify;
+ motionEvent.MotionEvent.display = DisplayHandle;
+ motionEvent.MotionEvent.window = xevent.ButtonEvent.window;
+ motionEvent.MotionEvent.x = xevent.ButtonEvent.x;
+ motionEvent.MotionEvent.y = xevent.ButtonEvent.y;
+ hwnd.Queue.EnqueueLocked (motionEvent);
break;
}
if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) || (xevent.CrossingEvent.window != hwnd.client_window)) {
goto ProcessNextMessage;
}
+ // If a grab is taking place, ignore it - we handle it in EnterNotify
+ if (Grab.Hwnd != IntPtr.Zero)
+ goto ProcessNextMessage;
+
+ // Reset the cursor explicitly on X11.
+ // X11 remembers the last set cursor for the window and in cases where
+ // the control won't get a WM_SETCURSOR X11 will restore the last
+ // known cursor, which we don't want.
+ //
+ SetCursor (hwnd.client_window, IntPtr.Zero);
+
msg.message=Msg.WM_MOUSELEAVE;
HoverState.Timer.Enabled = false;
HoverState.Window = IntPtr.Zero;
#if DriverDebugExtra
Console.WriteLine("GetMessage(): Window {0:X} ConfigureNotify x={1} y={2} width={3} height={4}", hwnd.client_window.ToInt32(), xevent.ConfigureEvent.x, xevent.ConfigureEvent.y, xevent.ConfigureEvent.width, xevent.ConfigureEvent.height);
#endif
-// if ((hwnd.x != xevent.ConfigureEvent.x) || (hwnd.y != xevent.ConfigureEvent.y) || (hwnd.width != xevent.ConfigureEvent.width) || (hwnd.height != xevent.ConfigureEvent.height)) {
- lock (hwnd.configure_lock) {
- SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
- hwnd.configure_pending = false;
- }
+ lock (hwnd.configure_lock) {
+ Form form = Control.FromHandle (hwnd.client_window) as Form;
+ if (form != null && !hwnd.resizing_or_moving) {
+ if (hwnd.x != form.Bounds.X || hwnd.y != form.Bounds.Y) {
+ SendMessage (form.Handle, Msg.WM_SYSCOMMAND, (IntPtr)SystemCommands.SC_MOVE, IntPtr.Zero);
+ hwnd.resizing_or_moving = true;
+ } else if (hwnd.width != form.Bounds.Width || hwnd.height != form.Bounds.Height) {
+ SendMessage (form.Handle, Msg.WM_SYSCOMMAND, (IntPtr)SystemCommands.SC_SIZE, IntPtr.Zero);
+ hwnd.resizing_or_moving = true;
+ }
+ if (hwnd.resizing_or_moving)
+ SendMessage (form.Handle, Msg.WM_ENTERSIZEMOVE, IntPtr.Zero, IntPtr.Zero);
+ }
+
+ SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero);
+ hwnd.configure_pending = false;
+
// We need to adjust our client window to track the resize of whole_window
if (hwnd.whole_window != hwnd.client_window)
PerformNCCalc(hwnd);
-// }
+ }
}
goto ProcessNextMessage;
}
}
goto ProcessNextMessage;
}
- Keyboard.FocusIn(FocusWindow);
SendMessage(FocusWindow, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero);
+ Keyboard.FocusIn (FocusWindow);
goto ProcessNextMessage;
}
if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear) {
goto ProcessNextMessage;
}
- Keyboard.FocusOut(FocusWindow);
while (Keyboard.ResetKeyState(FocusWindow, ref msg)) {
SendMessage(FocusWindow, msg.message, msg.wParam, msg.lParam);
}
+ Keyboard.FocusOut(hwnd.client_window);
SendMessage(FocusWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero);
goto ProcessNextMessage;
}
if (xevent.ClientMessageEvent.message_type == WM_PROTOCOLS) {
if (xevent.ClientMessageEvent.ptr1 == WM_DELETE_WINDOW) {
+ SendMessage (msg.hwnd, Msg.WM_SYSCOMMAND, (IntPtr)SystemCommands.SC_CLOSE, IntPtr.Zero);
msg.message = Msg.WM_CLOSE;
return true;
}
return true;
}
+ private HitTest NCHitTest (Hwnd hwnd, int x, int y)
+ {
+ // The hit test is sent in screen coordinates
+ IntPtr dummy;
+ int screen_x, screen_y;
+ XTranslateCoordinates (DisplayHandle, hwnd.WholeWindow, RootWindow, x, y, out screen_x, out screen_y, out dummy);
+ return (HitTest) NativeWindow.WndProc (hwnd.client_window, Msg.WM_NCHITTEST, IntPtr.Zero,
+ (IntPtr) (screen_y << 16 | screen_x & 0xFFFF));
+ }
+
internal override bool GetText(IntPtr handle, out string text) {
lock (XlibLock) {
hwnd = Hwnd.ObjectFromHandle(handle);
- AddExpose (hwnd, false, 0, 0, hwnd.Width, hwnd.Height);
+ AddExpose (hwnd, hwnd.WholeWindow == hwnd.ClientWindow, 0, 0, hwnd.Width, hwnd.Height);
}
internal override bool IsEnabled(IntPtr handle) {
if (queue == null) {
// This isn't really an error, MS doesn't start the timer if
- // it has no assosciated queue
+ // it has no assosciated queue. In this case, remove the timer
+ // from the list of unattached timers (if it was enabled).
+ lock (unattached_timer_list) {
+ if (unattached_timer_list.Contains (timer))
+ unattached_timer_list.Remove (timer);
+ }
return;
}
queue.timer_list.Remove (timer);
}
internal override void PostQuitMessage(int exitCode) {
- PostMessage (FosterParent, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero);
+ ApplicationContext ctx = Application.MWFThread.Current.Context;
+ Form f = ctx != null ? ctx.MainForm : null;
+ if (f != null)
+ PostMessage (Application.MWFThread.Current.Context.MainForm.window.Handle, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero);
+ else
+ PostMessage (FosterParent, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero);
XFlush(DisplayHandle);
}
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;
gc = XCreateGC(DisplayHandle, hwnd.client_window, IntPtr.Zero, ref gc_values);
- int src_x, src_y;
- int dest_x, dest_y;
- int width, height;
+ Rectangle visible_rect = GetTotalVisibleArea (hwnd.client_window);
+ visible_rect.Intersect (area);
- if (YAmount > 0) {
- src_y = area.Y;
- height = area.Height - YAmount;
- dest_y = area.Y + YAmount;
- }
- else {
- src_y = area.Y - YAmount;
- height = area.Height + YAmount;
- dest_y = area.Y;
- }
+ Rectangle dest_rect = visible_rect;
+ dest_rect.Y += YAmount;
+ dest_rect.X += XAmount;
+ dest_rect.Intersect (area);
- if (XAmount > 0) {
- src_x = area.X;
- width = area.Width - XAmount;
- dest_x = area.X + XAmount;
- }
- else {
- src_x = area.X - XAmount;
- width = area.Width + XAmount;
- dest_x = area.X;
- }
+ Point src = new Point (dest_rect.X - XAmount, dest_rect.Y - YAmount);
+ XCopyArea (DisplayHandle, hwnd.client_window, hwnd.client_window, gc, src.X, src.Y,
+ dest_rect.Width, dest_rect.Height, dest_rect.X, dest_rect.Y);
- XCopyArea(DisplayHandle, hwnd.client_window, hwnd.client_window, gc, src_x, src_y, width, height, dest_x, dest_y);
+ Rectangle dirty_area = GetDirtyArea (area, dest_rect, XAmount, YAmount);
+ AddExpose (hwnd, true, dirty_area.X, dirty_area.Y, dirty_area.Width, dirty_area.Height);
- // Generate an expose for the area exposed by the horizontal scroll
- // We don't use AddExpose since we're
- if (XAmount > 0) {
- AddExpose(hwnd, true, area.X, area.Y, XAmount, area.Height);
- } else if (XAmount < 0) {
- AddExpose(hwnd, true, XAmount + area.X + area.Width, area.Y, -XAmount, area.Height);
- }
+ ProcessGraphicsExpose (hwnd);
- // Generate an expose for the area exposed by the vertical scroll
- if (YAmount > 0) {
- AddExpose(hwnd, true, area.X, area.Y, area.Width, YAmount);
- } else if (YAmount < 0) {
- AddExpose(hwnd, true, area.X, YAmount + area.Y + area.Height, area.Width, -YAmount);
- }
XFreeGC(DisplayHandle, gc);
}
ScrollWindow(handle, rect, XAmount, YAmount, with_children);
}
+ Rectangle GetDirtyArea (Rectangle total_area, Rectangle valid_area, int XAmount, int YAmount)
+ {
+ Rectangle dirty_area = total_area;
+
+ if (YAmount > 0)
+ dirty_area.Height -= valid_area.Height;
+ else if (YAmount < 0) {
+ dirty_area.Height -= valid_area.Height;
+ dirty_area.Y += valid_area.Height;
+ }
+
+ if (XAmount > 0)
+ dirty_area.Width -= valid_area.Width;
+ else if (XAmount < 0) {
+ dirty_area.Width -= valid_area.Width;
+ dirty_area.X += valid_area.Width;
+ }
+
+ return dirty_area;
+ }
+
+ Rectangle GetTotalVisibleArea (IntPtr handle)
+ {
+ Control c = Control.FromHandle (handle);
+
+ Rectangle visible_area = c.ClientRectangle;
+ visible_area.Location = c.PointToScreen (Point.Empty);
+
+ for (Control parent = c.Parent; parent != null; parent = parent.Parent) {
+ if (!parent.IsHandleCreated || !parent.Visible)
+ return visible_area; // Non visible, not need to finish computations
+
+ Rectangle r = parent.ClientRectangle;
+ r.Location = parent.PointToScreen (Point.Empty);
+
+ visible_area.Intersect (r);
+ }
+
+ visible_area.Location = c.PointToClient (visible_area.Location);
+ return visible_area;
+ }
+
internal override void SendAsyncMethod (AsyncMethodData method) {
Hwnd hwnd;
XEvent xevent = new XEvent ();
internal override void SetBorderStyle(IntPtr handle, FormBorderStyle border_style) {
Form form = Control.FromHandle (handle) as Form;
- if (form != null && form.window_manager == null && (border_style == FormBorderStyle.FixedToolWindow ||
- border_style == FormBorderStyle.SizableToolWindow)) {
- form.window_manager = new ToolWindowManager (form);
+ if (form != null && form.window_manager == null) {
+ CreateParams cp = form.GetCreateParams ();
+ if (border_style == FormBorderStyle.FixedToolWindow ||
+ border_style == FormBorderStyle.SizableToolWindow ||
+ cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) {
+ form.window_manager = new ToolWindowManager (form);
+ }
}
RequestNCRecalc(handle);
return;
}
+ // Win32 doesn't do anything if disabled
+ if (!hwnd.enabled)
+ return;
+
prev_focus_window = FocusWindow;
FocusWindow = hwnd.client_window;
- Keyboard.FocusIn (FocusWindow);
if (prev_focus_window != IntPtr.Zero) {
SendMessage(prev_focus_window, Msg.WM_KILLFOCUS, FocusWindow, IntPtr.Zero);
}
SendMessage(FocusWindow, Msg.WM_SETFOCUS, prev_focus_window, IntPtr.Zero);
+ Keyboard.FocusIn (FocusWindow);
//XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).client_window, RevertTo.None, IntPtr.Zero);
}
if (queue == null) {
// This isn't really an error, MS doesn't start the timer if
- // it has no assosciated queue
+ // it has no assosciated queue at this stage (it will be
+ // enabled when a window is activated).
+ unattached_timer_list.Add (timer);
return;
}
queue.timer_list.Add (timer);
if (enabled) {
lock (XlibLock) {
- int[] atoms = new int[8];
- atoms[0] = _NET_WM_STATE_ABOVE.ToInt32();
- XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1);
+ if (hwnd.Mapped) {
+ SendNetWMMessage(hwnd.WholeWindow, _NET_WM_STATE, (IntPtr) NetWmStateRequest._NET_WM_STATE_ADD, _NET_WM_STATE_ABOVE, IntPtr.Zero);
+ } else {
+ int[] atoms = new int[8];
+ atoms[0] = _NET_WM_STATE_ABOVE.ToInt32();
+ XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1);
+ }
}
} else {
lock (XlibLock) {
- XDeleteProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE);
+ if (hwnd.Mapped)
+ SendNetWMMessage(hwnd.WholeWindow, _NET_WM_STATE, (IntPtr) NetWmStateRequest._NET_WM_STATE_REMOVE, _NET_WM_STATE_ABOVE, IntPtr.Zero);
+ else
+ XDeleteProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE);
}
}
return true;
// Oh boy.
if (hwnd.client_window != hwnd.whole_window) {
+ Keyboard.DestroyICForWindow (hwnd.client_window);
XDestroyWindow(DisplayHandle, hwnd.client_window);
hwnd.client_window = hwnd.whole_window;
}
[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
}
}