// Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2004 Novell, Inc. // // Authors: // Peter Bartok pbartok@novell.com // // // NOT COMPLETE using System; using System.Threading; using System.Drawing; using System.ComponentModel; using System.Collections; using System.Diagnostics; using System.Runtime.InteropServices; using System.Net; using System.Net.Sockets; // Only do the poll when building with mono for now #if __MonoCS__ using Mono.Unix; #endif /// X11 Version namespace System.Windows.Forms { internal class XplatUIX11 : XplatUIDriver { #region Structure Definitions internal struct Caret { internal Timer timer; // Blink interval internal IntPtr hwnd; // Window owning the caret internal int x; // X position of the caret internal int y; // Y position of the caret internal int width; // Width of the caret; if no image used internal int height; // Height of the caret, if no image used internal int visible; // Counter for visible/hidden internal bool on; // Caret blink display state: On/Off internal IntPtr gc; // Graphics context internal bool paused; // Don't update right now } internal struct Hover { internal Timer timer; // for hovering internal IntPtr hwnd; // Last window we entered; used to generate WM_MOUSEHOVER internal int x; // Last MouseMove X coordinate; used to generate WM_MOUSEHOVER internal int y; // Last MouseMove Y coordinate; used to generate WM_MOUSEHOVER internal int interval; // in milliseconds, how long to hold before hover is generated internal int hevent; // X Atom } #endregion // Structure Definitions #region Local Variables private static XplatUIX11 instance; private static int ref_count; private static bool themes_enabled; private static IntPtr DisplayHandle; // X11 handle to display private static int screen_num; // Screen number used private static IntPtr root_window; // Handle of the root window for the screen/display private static IntPtr FosterParent; // Container to hold child windows until their parent exists private static int wm_protocols; // X Atom private static int wm_delete_window; // X Atom private static int mwm_hints; // X Atom private static int wm_no_taskbar; // X Atom private static int wm_state_above; // X Atom private static int atom; // X Atom private static int net_wm_state; // X Atom private static int async_method; private static int post_message; private static uint default_colormap; // X Colormap ID internal static Keys key_state; internal static MouseButtons mouse_state; internal static Point mouse_position; internal static bool grab_confined; // Is the current grab (if any) confined to grab_area? internal static IntPtr grab_hwnd; // The window that is grabbed internal static Rectangle grab_area; // The area the current grab is confined to internal static IntPtr click_pending_hwnd; // internal static Msg click_pending_message; // internal static IntPtr click_pending_lparam; // internal static IntPtr click_pending_wparam; // internal static int click_pending_time; // Last time we received a mouse click internal static bool click_pending; // True if we haven't sent the last mouse click internal static int double_click_interval; // in milliseconds, how fast one has to click for a double click internal static Hover hover; internal static Caret caret; // To display a blinking caret private static Hashtable handle_data; private static XEventQueue message_queue; private X11Keyboard keyboard; private ArrayList timer_list; private Thread timer_thread; private AutoResetEvent timer_wait; private Socket listen; private Socket wake; #if __MonoCS__ private Pollfd [] pollfds; #endif private object xlib_lock = new object (); private static readonly EventMask SelectInputMask = EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.KeyPressMask | EventMask.KeyReleaseMask | EventMask.EnterWindowMask | EventMask.LeaveWindowMask | EventMask.ExposureMask | EventMask.PointerMotionMask | EventMask.VisibilityChangeMask | EventMask.StructureNotifyMask; #endregion // Local Variables #region Properties internal override Keys ModifierKeys { get { return keyboard.ModifierKeys; } } internal override MouseButtons MouseButtons { get { return mouse_state; } } internal override Point MousePosition { get { return mouse_position; } } internal override bool DropTarget { get { return false; } set { if (value) { throw new NotImplementedException("Need to figure out D'n'D for X11"); } } } // Keyboard internal override int KeyboardSpeed { get { // // A lot harder: need to do: // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1 // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8 // XkbGetControls(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0 // // And from that we can tell the repetition rate // // Notice, the values must map to: // [0, 31] which maps to 2.5 to 30 repetitions per second. // return 0; } } internal override int KeyboardDelay { get { // // Return values must range from 0 to 4, 0 meaning 250ms, // and 4 meaning 1000 ms. // return 1; // ie, 500 ms } } #endregion // Properties #region Constructor & Destructor // This is always called from a locked context private XplatUIX11() { // Handle singleton stuff first ref_count=0; message_queue = new XEventQueue (); timer_list = new ArrayList (); // Now regular initialization SetDisplay(XOpenDisplay(IntPtr.Zero)); keyboard = new X11Keyboard (DisplayHandle); listen = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); IPEndPoint ep = new IPEndPoint (IPAddress.Loopback, 0); listen.Bind (ep); listen.Listen (1); wake = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); wake.Connect (listen.LocalEndPoint); double_click_interval = 500; hover.interval = 500; hover.timer = new Timer(); hover.timer.Enabled = false; hover.timer.Interval = hover.interval; // FIXME - read this from somewhere hover.timer.Tick +=new EventHandler(MouseHover); hover.x = -1; hover.y = -1; #if __MonoCS__ pollfds = new Pollfd [2]; pollfds [0] = new Pollfd (); pollfds [0].fd = XConnectionNumber (DisplayHandle); pollfds [0].events = PollEvents.POLLIN; pollfds [1] = new Pollfd (); pollfds [1].fd = wake.Handle.ToInt32 (); pollfds [1].events = PollEvents.POLLIN; #endif caret.timer = new Timer(); caret.timer.Interval = 500; // FIXME - where should this number come from? caret.timer.Tick += new EventHandler(CaretCallback); } ~XplatUIX11() { lock (this) { if (DisplayHandle!=IntPtr.Zero) { XCloseDisplay(DisplayHandle); DisplayHandle=IntPtr.Zero; } } } #endregion // Constructor & Destructor #region Singleton Specific Code public static XplatUIX11 GetInstance() { lock (typeof (XplatUIX11)) { if (instance==null) { instance=new XplatUIX11(); } ref_count++; } return instance; } public int Reference { get { return ref_count; } } #endregion #region Events internal override event EventHandler Idle; #endregion // Events #region Callbacks private void MouseHover(object sender, EventArgs e) { if ((hover.x == mouse_position.X) && (hover.y == mouse_position.Y)) { XEvent xevent; hover.timer.Enabled = false; if (hover.hwnd != IntPtr.Zero) { xevent = new XEvent (); xevent.type = XEventName.ClientMessage; xevent.ClientMessageEvent.display = DisplayHandle; xevent.ClientMessageEvent.window = (IntPtr)hover.hwnd; xevent.ClientMessageEvent.message_type = (IntPtr)hover.hevent; xevent.ClientMessageEvent.format = 32; xevent.ClientMessageEvent.ptr1 = (IntPtr) (hover.y << 16 | hover.x); message_queue.EnqueueLocked (xevent); WakeupMain (); } } } private void CaretCallback(object sender, EventArgs e) { if (caret.paused) { return; } caret.on = !caret.on; XDrawLine(DisplayHandle, caret.hwnd, caret.gc, caret.x, caret.y, caret.x, caret.y + caret.height); } #endregion // Callbacks #region Public Static Methods internal override IntPtr InitializeDriver() { lock (this) { if (DisplayHandle==IntPtr.Zero) { DisplayHandle=XOpenDisplay(IntPtr.Zero); key_state=Keys.None; mouse_state=MouseButtons.None; mouse_position=Point.Empty; } } return IntPtr.Zero; } internal static void SetDisplay(IntPtr display_handle) { if (display_handle != IntPtr.Zero) { IntPtr Screen; if (FosterParent != IntPtr.Zero) { XDestroyWindow(DisplayHandle, FosterParent); } if (DisplayHandle != IntPtr.Zero) { XCloseDisplay(DisplayHandle); } DisplayHandle=display_handle; // We need to tell System.Drawing our DisplayHandle. FromHdcInternal has // been hacked to do this for us. Graphics.FromHdcInternal (DisplayHandle); // Create a few things key_state = Keys.None; mouse_state = MouseButtons.None; mouse_position = Point.Empty; Screen = XDefaultScreenOfDisplay(DisplayHandle); //screen_num = XScreenNumberOfScreen(DisplayHandle, Screen); screen_num = 0; root_window = XRootWindow(display_handle, screen_num); default_colormap = XDefaultColormap(display_handle, screen_num); // Create the foster parent FosterParent=XCreateSimpleWindow(display_handle, root_window, 0, 0, 1, 1, 4, 0, 0); if (FosterParent==IntPtr.Zero) { Console.WriteLine("XplatUIX11 Constructor failed to create FosterParent"); } // Prepare for shutdown wm_protocols=XInternAtom(display_handle, "WM_PROTOCOLS", false); wm_delete_window=XInternAtom(display_handle, "WM_DELETE_WINDOW", false); // handling decorations and such mwm_hints=XInternAtom(display_handle, "_MOTIF_WM_HINTS", false); net_wm_state=XInternAtom(display_handle, "_NET_WM_STATE", false); wm_no_taskbar=XInternAtom(display_handle, "_NET_WM_STATE_NO_TASKBAR", false); wm_state_above=XInternAtom(display_handle, "_NET_WM_STATE_ABOVE", false); atom=XInternAtom(display_handle, "ATOM", false); async_method = XInternAtom(display_handle, "_SWF_AsyncAtom", false); post_message = XInternAtom (display_handle, "_SWF_PostMessageAtom", false); hover.hevent = XInternAtom(display_handle, "_SWF_HoverAtom", false); handle_data = new Hashtable (); } else { throw new ArgumentNullException("Display", "Could not open display (X-Server required. Check you DISPLAY environment variable)"); } } internal override void ShutdownDriver(IntPtr token) { lock (this) { if (DisplayHandle!=IntPtr.Zero) { XCloseDisplay(DisplayHandle); DisplayHandle=IntPtr.Zero; } } } internal void Version() { Console.WriteLine("Xplat version $revision: $"); } internal override void Exit() { Console.WriteLine("XplatUIX11.Exit"); } internal override void GetDisplaySize(out Size size) { XWindowAttributes attributes=new XWindowAttributes(); lock (xlib_lock) { XGetWindowAttributes(DisplayHandle, XRootWindow(DisplayHandle, 0), ref attributes); } size = new Size(attributes.width, attributes.height); } internal override void EnableThemes() { themes_enabled=true; } internal override IntPtr CreateWindow(CreateParams cp) { IntPtr WindowHandle; IntPtr ParentHandle; int X; int Y; int Width; int Height; MotifWmHints mwmHints; uint[] atoms; int atom_count; int BorderWidth; int protocols; XSetWindowAttributes attr; ParentHandle=cp.Parent; X=cp.X; Y=cp.Y; Width=cp.Width; Height=cp.Height; BorderWidth=0; if (Width<1) Width=1; if (Height<1) Height=1; lock (xlib_lock) { if (ParentHandle==IntPtr.Zero) { if ((cp.Style & (int)(WindowStyles.WS_CHILD))!=0) { // We need to use our foster parent window until // this poor child gets it's parent assigned ParentHandle=FosterParent; } else if ((cp.Style & (int)(WindowStyles.WS_POPUP))!=0) { BorderWidth=0; ParentHandle=XRootWindow(DisplayHandle, 0); } else { if (X<1) X=50; if (Y<1) Y=50; BorderWidth=4; ParentHandle=XRootWindow(DisplayHandle, 0); } } attr = new XSetWindowAttributes(); if ((cp.ExStyle & ((int)WindowStyles.WS_EX_TOOLWINDOW)) != 0) { attr.save_under = true; } attr.override_redirect = false; if ((cp.Style & ((int)WindowStyles.WS_POPUP)) != 0) { attr.override_redirect = true; } attr.bit_gravity = Gravity.NorthWestGravity; attr.win_gravity = Gravity.NorthWestGravity; WindowHandle=XCreateWindow(DisplayHandle, ParentHandle, X, Y, Width, Height, BorderWidth, (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, IntPtr.Zero, SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity | SetWindowValuemask.SaveUnder | SetWindowValuemask.OverrideRedirect, ref attr); // Set the appropriate window manager hints if (((cp.Style & ((int)WindowStyles.WS_POPUP)) != 0) && (ParentHandle != IntPtr.Zero)) { XSetTransientForHint(DisplayHandle, WindowHandle, ParentHandle); } mwmHints = new MotifWmHints(); mwmHints.flags = MotifFlags.Functions | MotifFlags.Decorations; mwmHints.functions = 0; mwmHints.decorations = 0; if ((cp.Style & ((int)WindowStyles.WS_CAPTION)) != 0) { mwmHints.functions |= MotifFunctions.Move; mwmHints.decorations |= MotifDecorations.Title | MotifDecorations.Menu; } if ((cp.Style & ((int)WindowStyles.WS_THICKFRAME)) != 0) { mwmHints.functions |= MotifFunctions.Move | MotifFunctions.Resize; mwmHints.decorations |= MotifDecorations.Border | MotifDecorations.ResizeH; } if ((cp.Style & ((int)WindowStyles.WS_MINIMIZEBOX)) != 0) { mwmHints.functions |= MotifFunctions.Minimize; mwmHints.decorations |= MotifDecorations.Minimize; } if ((cp.Style & ((int)WindowStyles.WS_MAXIMIZEBOX)) != 0) { mwmHints.functions |= MotifFunctions.Maximize; mwmHints.decorations |= MotifDecorations.Maximize; } if ((cp.Style & ((int)WindowStyles.WS_SYSMENU)) != 0) { mwmHints.functions |= MotifFunctions.Close; } if ((cp.ExStyle & ((int)WindowStyles.WS_EX_DLGMODALFRAME)) != 0) { mwmHints.decorations |= MotifDecorations.Border; } if ((cp.Style & ((int)WindowStyles.WS_DLGFRAME)) != 0) { mwmHints.decorations |= MotifDecorations.Border; } if ((cp.Style & ((int)WindowStyles.WS_BORDER)) != 0) { mwmHints.decorations |= MotifDecorations.Border; } if ((cp.ExStyle & ((int)WindowStyles.WS_EX_TOOLWINDOW)) != 0) { mwmHints.functions = 0; mwmHints.decorations = 0; } XChangeProperty(DisplayHandle, WindowHandle, mwm_hints, mwm_hints, 32, PropertyMode.Replace, ref mwmHints, 5); atoms = new uint[8]; atom_count = 0; if ((cp.ExStyle & ((int)WindowStyles.WS_EX_TOOLWINDOW)) != 0) { atoms[atom_count++] = (uint)wm_state_above; atoms[atom_count++] = (uint)wm_no_taskbar; } XChangeProperty(DisplayHandle, WindowHandle, net_wm_state, atom, 32, PropertyMode.Replace, ref atoms, atom_count); XMapWindow(DisplayHandle, WindowHandle); XSelectInput(DisplayHandle, WindowHandle, SelectInputMask); protocols=wm_delete_window; XSetWMProtocols(DisplayHandle, WindowHandle, ref protocols, 1); } return(WindowHandle); } internal override IntPtr CreateWindow(IntPtr Parent, int X, int Y, int Width, int Height) { CreateParams create_params = new CreateParams(); create_params.Caption = ""; create_params.X = X; create_params.Y = Y; create_params.Width = Width; create_params.Height = Height; create_params.ClassName=XplatUI.DefaultClassName; create_params.ClassStyle = 0; create_params.ExStyle=0; create_params.Parent=IntPtr.Zero; create_params.Param=0; return CreateWindow(create_params); } internal override void DestroyWindow(IntPtr handle) { lock (this) { HandleData data = (HandleData) handle_data [handle]; if (data != null) { data.Dispose (); handle_data [handle] = null; XDestroyWindow(DisplayHandle, handle); } } } internal override void RefreshWindow(IntPtr handle) { XEvent xevent = new XEvent(); IntPtr root; int border_width; int depth; int x; int y; int width; int height; lock (xlib_lock) { // We need info about our window to generate the expose XGetGeometry(DisplayHandle, handle, out root, out x, out y, out width, out height, out border_width, out depth); xevent.type=XEventName.Expose; xevent.ExposeEvent.display=DisplayHandle; xevent.ExposeEvent.window=handle; xevent.ExposeEvent.x=0; xevent.ExposeEvent.y=0; xevent.ExposeEvent.width=width; xevent.ExposeEvent.height=height; XSendEvent(DisplayHandle, handle, false, EventMask.ExposureMask, ref xevent); XFlush(DisplayHandle); } } internal override void SetWindowBackground(IntPtr handle, Color color) { XColor xcolor; xcolor = new XColor(); xcolor.red = (ushort)(color.R * 257); xcolor.green = (ushort)(color.G * 257); xcolor.blue = (ushort)(color.B * 257); XAllocColor(DisplayHandle, default_colormap, ref xcolor); lock (xlib_lock) { XSetWindowBackground(DisplayHandle, handle, xcolor.pixel); XClearWindow(DisplayHandle, handle); } } [MonoTODO("Add support for internal table of windows/DCs for looking up paint area and cleanup")] internal override PaintEventArgs PaintEventStart(IntPtr handle) { PaintEventArgs paint_event; HandleData data = (HandleData) handle_data [handle]; if (data == null) { throw new Exception ("null data on paint event start: " + handle); } if (caret.visible == 1) { caret.paused = true; HideCaret(); } data.DeviceContext = Graphics.FromHwnd (handle); paint_event = new PaintEventArgs((Graphics)data.DeviceContext, data.InvalidArea); return paint_event; } internal override void PaintEventEnd(IntPtr handle) { HandleData data = (HandleData) handle_data [handle]; if (data == null) throw new Exception ("null data on PaintEventEnd"); data.ClearInvalidArea (); Graphics g = (Graphics) data.DeviceContext; g.Flush (); g.Dispose (); if (caret.visible == 1) { ShowCaret(); caret.paused = false; } } internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) { // X requires a sanity check for width & height; otherwise it dies if (width < 1) { width = 1; } if (height < 1) { height = 1; } lock (xlib_lock) { XMoveResizeWindow(DisplayHandle, handle, x, y, width, height); } return; } internal override void GetWindowPos(IntPtr handle, out int x, out int y, out int width, out int height, out int client_width, out int client_height) { IntPtr root; int border_width; int depth; lock (xlib_lock) { XGetGeometry(DisplayHandle, handle, out root, out x, out y, out width, out height, out border_width, out depth); } client_width = width; client_height = height; return; } internal override void Activate(IntPtr handle) { lock (xlib_lock) { // Not sure this is the right method, but we don't use ICs either... XRaiseWindow(DisplayHandle, handle); } return; } internal override void EnableWindow(IntPtr handle, bool Enable) { // We do nothing; On X11 SetModal is used to create modal dialogs, on Win32 this function is used (see comment there) } internal override void SetModal(IntPtr handle, bool Modal) { // We need to use the Motif window manager hints to build modal stuff; see freedesktop.org throw new NotImplementedException("Finish me"); } internal override void Invalidate (IntPtr handle, Rectangle rc, bool clear) { if (clear) { XClearArea (DisplayHandle, handle, rc.Left, rc.Top, (uint)rc.Width, (uint)rc.Height, true); } else { XEvent xevent = new XEvent (); xevent.type = XEventName.Expose; xevent.ExposeEvent.display = DisplayHandle; xevent.ExposeEvent.window = handle; xevent.ExposeEvent.x = rc.X; xevent.ExposeEvent.y = rc.Y; xevent.ExposeEvent.width = rc.Width; xevent.ExposeEvent.height = rc.Height; AddExpose (xevent); } } internal override IntPtr DefWndProc(ref Message msg) { switch((Msg)msg.Msg) { case Msg.WM_ERASEBKGND: { HandleData data = (HandleData) handle_data [msg.HWnd]; if (data == null) { throw new Exception ("null data on WM_ERASEBKGND: " + msg.HWnd); } XClearArea(DisplayHandle, msg.HWnd, data.InvalidArea.Left, data.InvalidArea.Top, (uint)data.InvalidArea.Width, (uint)data.InvalidArea.Height, false); return IntPtr.Zero; } } return IntPtr.Zero; } internal override void HandleException(Exception e) { StackTrace st = new StackTrace(e); Console.WriteLine("Exception '{0}'", e.Message+st.ToString()); Console.WriteLine("{0}{1}", e.Message, st.ToString()); } internal override void DoEvents() { Console.WriteLine("XplatUIX11.DoEvents"); } internal override bool PeekMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) { Console.WriteLine("XplatUIX11.PeekMessage"); return true; } private void CreateKeyBoardMsg (XEvent xevent, ref MSG msg) { IntPtr buffer = Marshal.AllocHGlobal(24); XKeySym keysym; string keys; int len; msg.wParam = IntPtr.Zero; lock (xlib_lock) { len = XLookupString(ref xevent, buffer, 24, out keysym, IntPtr.Zero); } if ((keysym==XKeySym.XK_Control_L) || (keysym==XKeySym.XK_Control_R)) { if (xevent.type==XEventName.KeyPress) { key_state |= Keys.Control; } else { key_state &= ~Keys.Control; } } if ((keysym==XKeySym.XK_Shift_L) || (keysym==XKeySym.XK_Shift_R)) { if (xevent.type==XEventName.KeyPress) { key_state |= Keys.Shift; } else { key_state &= ~Keys.Shift; } } if ((keysym==XKeySym.XK_Alt_L) || (keysym==XKeySym.XK_Alt_R)) { if (xevent.type==XEventName.KeyPress) { key_state |= Keys.Alt; } else { key_state &= ~Keys.Alt; } } if (len>0) { /* String is not zero terminated*/ Marshal.WriteByte (buffer, len, 0); } keys=Marshal.PtrToStringAuto(buffer); for (int i = 0; i < KeyMapping.Length; i++) { if (KeyMapping[i].X11Key == keysym) { msg.wParam = (IntPtr) KeyMapping[i].Win32Key; // Console.WriteLine("Got special key {0} {1:x} ", (VirtualKeys) keysym, keysym); break; } } if ((msg.wParam == IntPtr.Zero) && (keys.Length>0)) { char[] keychars; keychars=keys.ToCharArray(0, 1); msg.wParam=(IntPtr)keychars[0]; // Console.WriteLine("Got key {0} {1:x} ", (VirtualKeys) keysym, keysym); } Marshal.FreeHGlobal (buffer); msg.lParam = (IntPtr) 1; } private IntPtr GetMousewParam(int Delta) { int result = 0; if ((mouse_state & MouseButtons.Left) != 0) { result |= (int)MsgButtons.MK_LBUTTON; } if ((mouse_state & MouseButtons.Middle) != 0) { result |= (int)MsgButtons.MK_MBUTTON; } if ((mouse_state & MouseButtons.Right) != 0) { result |= (int)MsgButtons.MK_RBUTTON; } if ((key_state & Keys.Control) != 0) { result |= (int)MsgButtons.MK_CONTROL; } if ((key_state & Keys.Shift) != 0) { result |= (int)MsgButtons.MK_SHIFT; } result |= Delta << 16; return (IntPtr)result; } private int NextTimeout (DateTime now) { int timeout = Int32.MaxValue; lock (timer_list) { foreach (Timer timer in timer_list) { int next = (int) (timer.Expires - now).TotalMilliseconds; if (next < 0) return 0; // Have a timer that has already expired if (next < timeout) timeout = next; } } if (timeout < Timer.Minimum) timeout = Timer.Minimum; return timeout; } private void CheckTimers (DateTime now) { lock (timer_list) { int count = timer_list.Count; if (count == 0) return; for (int i = 0; i < timer_list.Count; i++) { Timer timer = (Timer) timer_list [i]; if (timer.Enabled && timer.Expires <= now) { timer.FireTick (); timer.Update (now); } } } } private void AddExpose (XEvent xevent) { HandleData data = (HandleData) handle_data [xevent.AnyEvent.window]; if (data == null) { data = new HandleData (); handle_data [xevent.AnyEvent.window] = data; } if (!data.IsVisible) { return; } data.AddToInvalidArea (xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); if (!data.HasExpose) { message_queue.Enqueue (xevent); data.HasExpose = true; } } private void UpdateMessageQueue () { DateTime now = DateTime.Now; int pending; lock (xlib_lock) { pending = XPending (DisplayHandle); } if (pending == 0) { if (Idle != null) { Idle (this, EventArgs.Empty); } lock (xlib_lock) { pending = XPending (DisplayHandle); } } if (pending == 0) { int timeout = NextTimeout (now); if (timeout > 0) { #if __MonoCS__ Syscall.poll (pollfds, (uint) pollfds.Length, timeout); #endif pending = XPending (DisplayHandle); } } CheckTimers (now); if (pending == 0) { lock (xlib_lock) { pending = XPending (DisplayHandle); } } while (pending > 0) { XEvent xevent = new XEvent (); lock (xlib_lock) { XNextEvent (DisplayHandle, ref xevent); } switch (xevent.type) { case XEventName.Expose: AddExpose (xevent); break; case XEventName.KeyPress: case XEventName.KeyRelease: case XEventName.ButtonPress: case XEventName.ButtonRelease: case XEventName.MotionNotify: case XEventName.EnterNotify: case XEventName.LeaveNotify: case XEventName.ConfigureNotify: case XEventName.DestroyNotify: case XEventName.FocusIn: case XEventName.FocusOut: case XEventName.ClientMessage: message_queue.Enqueue (xevent); break; } lock (xlib_lock) { pending = XPending (DisplayHandle); } } } internal override bool GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) { XEvent xevent; if (message_queue.Count > 0) { xevent = (XEvent) message_queue.Dequeue (); } else { UpdateMessageQueue (); if (message_queue.Count > 0) { xevent = (XEvent) message_queue.Dequeue (); } else { msg.hwnd= IntPtr.Zero; msg.message = Msg.WM_ENTERIDLE; return true; } } msg.hwnd=xevent.AnyEvent.window; // // 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. // switch(xevent.type) { case XEventName.KeyPress: { keyboard.KeyEvent (xevent.AnyEvent.window, xevent, ref msg); break; } case XEventName.KeyRelease: { keyboard.KeyEvent (xevent.AnyEvent.window, xevent, ref msg); break; } case XEventName.ButtonPress: { switch(xevent.ButtonEvent.button) { case 1: { mouse_state |= MouseButtons.Left; msg.message=Msg.WM_LBUTTONDOWN; msg.wParam=GetMousewParam(0); break; } case 2: { mouse_state |= MouseButtons.Middle; msg.message=Msg.WM_MBUTTONDOWN; msg.wParam=GetMousewParam(0); break; } case 3: { mouse_state |= MouseButtons.Right; msg.message=Msg.WM_RBUTTONDOWN; msg.wParam=GetMousewParam(0); break; } case 4: { msg.message=Msg.WM_MOUSEWHEEL; msg.wParam=GetMousewParam(120); break; } case 5: { msg.message=Msg.WM_MOUSEWHEEL; msg.wParam=GetMousewParam(-120); break; } } msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); mouse_position.X=xevent.ButtonEvent.x; mouse_position.Y=xevent.ButtonEvent.y; if (!click_pending) { click_pending = true; click_pending_hwnd = msg.hwnd; click_pending_message = msg.message; click_pending_wparam = msg.wParam; click_pending_lparam = msg.lParam; click_pending_time = xevent.ButtonEvent.time; } else { if (((xevent.ButtonEvent.time - click_pending_time)