// Handle a move/resize operation from "CaptionWidget". internal void HandleMoveResize(int x, int y, int width, int height) { bool changed = (x != this.x || y != this.y || width != this.width || height != this.height); this.x = x; this.y = y; this.width = width; this.height = height; if(changed) { // Queue up the "OnMoveResize" for the next idle period. if(resizeTimer == null) { resizeTimer = new Timer (new TimerCallback(PerformResize), null, 0, -1); } } }
// Perform an actual resize operation. This will be called at the // next convenient idle period, to avoid overflowing the event queue. private void PerformResize(Object state) { if(resizeTimer != null) { resizeTimer.Stop(); resizeTimer = null; } expectedWidth = -1; expectedHeight = -1; if(mdiClient != null) { mdiClient.PositionControls(); } OnMoveResize(x, y, width, height); }
// Dispatch an event to this widget. internal override void DispatchEvent(ref XEvent xevent) { XKeySym keysym; Widget widget; InputOnlyWidget io; Xlib.Xlong[] data; switch((Xsharp.Events.EventType)(xevent.xany.type__)) { case Xsharp.Events.EventType.ClientMessage: { // Handle messages from the window manager. if(xevent.xclient.message_type == dpy.wmProtocols) { if(xevent.xclient.l(0) == (int)(dpy.wmDeleteWindow)) { // User wants the window to close. Close(); } else if(xevent.xclient.l(0) == (int)(dpy.wmTakeFocus)) { // We were given the primary input focus. PrimaryFocusIn(); } else if(xevent.xclient.l(0) == (int)(dpy.wmContextHelp)) { // The user pressed the "help" button. OnHelp(); } else if(xevent.xclient.l(0) == (int)(dpy.wmPing)) { // The window manager has pinged us to see // if we are still responding or are dead. // We send the message straight back to the WM. try { IntPtr display = dpy.Lock(); xevent.xany.window = screen.RootWindow.GetWidgetHandle(); Xlib.XSendEvent (display, xevent.xany.window, XBool.False, (int)(EventMask.NoEventMask), ref xevent); } finally { dpy.Unlock(); } } } } break; case Xsharp.Events.EventType.PropertyNotify: { // Handle a property change notification. if(xevent.xproperty.atom == dpy.wmState) { // The "WM_STATE" property has changed. if(xevent.xproperty.state == 0) { // New value for the window state. data = GetWindowProperty(dpy.wmState); if(data.Length >= 1 && data[0] == (Xlib.Xlong)3) { // The window is now in the iconic state. if(!iconic) { iconic = true; OnIconicStateChanged(true); } } else { // The window is now in the normal state. if(iconic) { iconic = false; OnIconicStateChanged(false); } } } else { // Property removed, so it is "normal" now. if(iconic) { iconic = false; OnIconicStateChanged(false); } } } else if(xevent.xproperty.atom == dpy.wmNetState) { // The "_NET_WM_STATE" property has changed. if(xevent.xproperty.state == 0) { // New value: look for maximized state atoms. data = GetWindowProperty(dpy.wmNetState); if(ContainsMaximizedAtom(data)) { // The window is now maximized. if(!maximized) { maximized = true; OnMaximizedStateChanged(true); } } else { // The window has been restored. if(maximized) { maximized = false; OnMaximizedStateChanged(false); } } } else { // Value removed, so not maximized any more. if(maximized) { maximized = false; OnMaximizedStateChanged(false); } } } else if(xevent.xclient.message_type == dpy.internalBeginInvoke) { OnBeginInvokeMessage((IntPtr)xevent.xclient.l(0)); } } break; case Xsharp.Events.EventType.FocusIn: { // This window has received the focus. PrimaryFocusIn(); } break; case Xsharp.Events.EventType.FocusOut: { // This window has lost the focus. if(hasPrimaryFocus) { hasPrimaryFocus = false; if(focusWidget != null) { focusWidget.DispatchFocusOut(null); } OnPrimaryFocusOut(); } } break; case Xsharp.Events.EventType.KeyPress: { // Convert the event into a symbol and a string. if(keyBuffer == IntPtr.Zero) { keyBuffer = Marshal.AllocHGlobal(32); } keysym = 0; int len = Xlib.XLookupString (ref xevent.xkey, keyBuffer, 32, ref keysym, IntPtr.Zero); String str; if(len > 0) { str = Marshal.PtrToStringAnsi(keyBuffer, len); } else { str = null; } // Special case: check for Alt+F4 to close the window. // Some window managers trap Alt+F4 themselves, but not // all. People who are used to System.Windows.Forms // under Windows expect Alt+F4 to close the window, // irrespective of what key the window manager uses. // // Note: this check is not foolproof. The window // manager or the kernel may have redirected Alt+F4 // for some other purpose (e.g. switching between // virtual consoles). On such systems, there is // nothing that we can do to get the key event and // this code will never be called. // if((((KeyName)keysym) == KeyName.XK_F4 || ((KeyName)keysym) == KeyName.XK_KP_F4) && (xevent.xkey.state & ModifierMask.Mod1Mask) != 0) { Close(); break; } // If we have an MDI client, then give it a chance // to process the keypress just in case it is // something like Ctrl+F4 or Ctrl+Tab. if(mdiClient != null) { if(mdiClient.DispatchKeyEvent ((KeyName)keysym, xevent.xkey.state, str)) { break; } } // Dispatch the event. widget = focusWidget; while(widget != null) { io = (widget as InputOnlyWidget); if(io != null) { if(io.DispatchKeyEvent ((KeyName)keysym, xevent.xkey.state, str)) { break; } } if(widget == this) { break; } widget = widget.Parent; } } break; case Xsharp.Events.EventType.KeyRelease: { // Convert the event into a symbol and a string. if(keyBuffer == IntPtr.Zero) { keyBuffer = Marshal.AllocHGlobal(32); } keysym = 0; int len = Xlib.XLookupString (ref xevent.xkey, keyBuffer, 32, ref keysym, IntPtr.Zero); // Dispatch the event. widget = focusWidget; while(widget != null) { io = (widget as InputOnlyWidget); if(io != null) { if(io.DispatchKeyReleaseEvent ((KeyName)keysym, xevent.xkey.state)) { break; } } if(widget == this) { break; } widget = widget.Parent; } } break; case Xsharp.Events.EventType.ButtonPress: { if ((xevent.xbutton.button == ButtonName.Button4) || (xevent.xbutton.button == ButtonName.Button5)) return; } break; case Xsharp.Events.EventType.ButtonRelease: { // Handle mouse wheel events. // Sanity check if ((xevent.xbutton.button != ButtonName.Button4) && (xevent.xbutton.button != ButtonName.Button5)) break; // Dispatch the event. widget = focusWidget; while(widget != null) { io = (widget as InputOnlyWidget); if (io != null) { if (io.DispatchWheelEvent (ref xevent)) { return; } } if (widget == this) { break; } widget = widget.Parent; } } break; case Xsharp.Events.EventType.ConfigureNotify: { // The window manager may have caused us to move/resize. if(xevent.xconfigure.window != xevent.window) { // SubstructureNotify - not interesting to us. break; } if(Parent is CaptionWidget) { // Ignore configure events if we are an MDI child. break; } if(xevent.xconfigure.width != width || xevent.xconfigure.height != height || expectedWidth != -1) { // The size has been changed by the window manager. if(expectedWidth == -1) { // Resize from the window manager, not us. width = xevent.xconfigure.width; height = xevent.xconfigure.height; } else if(expectedWidth == xevent.xconfigure.width && expectedHeight == xevent.xconfigure.height) { // This is the size that we were expecting. // Further ConfigureNotify's will be from // the window manager instead of from us. expectedWidth = -1; expectedHeight = -1; } if(resizeTimer == null) { resizeTimer = new Timer (new TimerCallback(PerformResize), null, 0, -1); } } if(xevent.send_event || !reparented) { // The window manager moved us to a new position. if(x != xevent.xconfigure.x || y != xevent.xconfigure.y) { x = xevent.xconfigure.x; y = xevent.xconfigure.y; OnMoveResize(x, y, width, height); } } } break; case Xsharp.Events.EventType.ReparentNotify: { // We may have been reparented by the window manager. if(xevent.xreparent.window != (XWindow)handle) { // SubstructureNotify - not interesting to us. break; } if(xevent.xreparent.parent != (XWindow)(screen.RootWindow.handle)) { // Reparented by the window manager. reparented = true; } else { // Window manager crashed: we are back on the root. reparented = false; x = xevent.xreparent.x; y = xevent.xreparent.y; OnMoveResize(x, y, width, height); } } break; case Xsharp.Events.EventType.MapNotify: { // The window manager mapped us to the screen. if(Parent is CaptionWidget) { break; } if(iconic) { iconic = false; OnIconicStateChanged(false); } if(!mapped) { mapped = true; OnMapStateChanged(); } } break; case Xsharp.Events.EventType.UnmapNotify: { // We were unmapped from the screen. If "mapped" // is true, then we are being iconified by the window // manager. Otherwise, we asked to be withdrawn. if(Parent is CaptionWidget) { break; } if(!iconic && mapped) { iconic = true; OnIconicStateChanged(true); } } break; } base.DispatchEvent(ref xevent); }
// Internal constructor, that can create a top-level window on either // the root window or underneath an MDI client parent. public TopLevelWindow(Widget parent, String name, int x, int y, int width, int height) : base(parent, x, y, width, height, new Color(StandardColor.Foreground), new Color(StandardColor.Background), true, false) { // Check the parent. if(!(parent is RootWindow) && !(parent is CaptionWidget)) { throw new XInvalidOperationException(); } // Initialize this object's state. this.name = ((name != null) ? name : String.Empty); this.iconic = false; this.maximized = false; this.hasPrimaryFocus = false; this.reparented = false; this.sticky = false; this.shaded = false; this.hidden = false; this.fullScreen = false; this.keyBuffer = IntPtr.Zero; this.focusWidget = this; this.defaultFocus = null; this.decorations = MotifDecorations.All; this.functions = MotifFunctions.All; this.inputType = MotifInputType.Normal; this.transientFor = null; this.resizeTimer = null; this.expectedWidth = -1; this.expectedHeight = -1; // Top-level widgets receive all key and focus events. SelectInput(EventMask.KeyPressMask | EventMask.KeyReleaseMask | EventMask.FocusChangeMask | EventMask.StructureNotifyMask | EventMask.PropertyChangeMask); // We don't use WM properties if the parent is a CaptionWidget. if(parent is CaptionWidget) { return; } // Set the initial WM properties. try { // Lock down the display and get the window handle. IntPtr display = dpy.Lock(); XWindow handle = GetWidgetHandle(); // Make this the group leader if we don't have one yet. bool isFirst = false; if(dpy.groupLeader == XWindow.Zero) { dpy.groupLeader = handle; isFirst = true; } // Set the WM_CLASS hint. Application app = Application.Primary; if(app != null) { XClassHint classHint = new XClassHint(); classHint.res_name = app.ResourceName; classHint.res_class = app.ResourceClass; Xlib.XSetClassHint(display, handle, ref classHint); classHint.Free(); } // Set the title bar and icon names. SetWindowName(display, handle, this.name); // Ask for "WM_DELETE_WINDOW" and "WM_TAKE_FOCUS". SetProtocols(display, handle); // Set the window hints. if(isFirst && app != null && app.StartIconic) { // The user supplied "-iconic" on the command-line. iconic = true; } SetWMHints(display, handle); // Set some other string properties. String cultureName = CultureInfo.CurrentCulture.Name; if(cultureName == null || (cultureName.Length == 0)) { cultureName = "en_US"; } else { cultureName = cultureName.Replace("-", "_"); } SetTextProperty(display, handle, "WM_LOCALE_NAME", cultureName); String hostname = Application.Hostname; if(hostname != null) { SetTextProperty(display, handle, "WM_CLIENT_MACHINE", hostname); } if(isFirst) { String[] args = Environment.GetCommandLineArgs(); if(args != null && args.Length > 0) { // We put "ilrun" at the start of the command, // because the command needs to be in a form // that can be directly executed by fork/exec, // and IL binaries normally aren't in this form. String[] newArgs = new String [args.Length + 1]; newArgs[0] = "ilrun"; Array.Copy(args, 0, newArgs, 1, args.Length); SetTextProperty(display, handle, "WM_COMMAND", newArgs); } } #if CONFIG_EXTENDED_DIAGNOSTICS // Put the process ID on the window. int pid = Process.GetCurrentProcess().Id; if(pid != -1 && pid != 0) { Xlib.XChangeProperty (display, handle, Xlib.XInternAtom(display, "_NET_WM_PID", XBool.False), Xlib.XInternAtom(display, "CARDINAL", XBool.False), 32, 0 /* PropModeReplace */, new Xlib.Xlong [] {(Xlib.Xlong)(pid)}, 1); } #endif } finally { dpy.Unlock(); } }
// fix all timers in the queue if the system time has been changed internal static void FixTimers( Timer timer ) { DateTime fixTime = DateTime.UtcNow; long tpms = TimeSpan.TicksPerMillisecond; while( null != timer ) { timer.nextDue = fixTime + new TimeSpan(timer.period * tpms); timer = timer.next; } }
// Activate timers that have fired on a particular display. // We assume that this is called with the display lock. internal static bool ActivateTimers(Display dpy) { // Bail out early if there are no timers, to avoid // calling "DateTime.UtcNow" if we don't need to. if(dpy.timerQueue == null) { return false; } DateTime now = DateTime.UtcNow; Timer timer; DateTime next; bool activated = false; for(;;) { // Remove the first timer from the queue if // it has expired. Bail out if it hasn't. timer = dpy.timerQueue; if(timer == null) { break; } else if(timer.nextDue <= now) { timer.RemoveTimer(); } else { break; } // Invoke the timer's callback delegate. activated = true; if(timer.callback is TimerCallback) { TimerCallback cb1 = timer.callback as TimerCallback; dpy.Unlock(); try { cb1(timer.state); } finally { dpy.Lock(); } } else { EventHandler cb2 = timer.callback as EventHandler; dpy.Unlock(); try { cb2(timer.state, EventArgs.Empty); } finally { dpy.Lock(); } } // Add the timer back onto the queue if necessary. if(!timer.stopped && !timer.onDisplayQueue) { if(timer.period < 0) { timer.stopped = true; } else { next = timer.nextDue + new TimeSpan(timer.period * TimeSpan.TicksPerMillisecond); // if the next due is less than now, the date/time may have changed. // since the timer expired right now the next due might be now + period if(next <= now) { next += new TimeSpan (((((Int64)(now - next).TotalMilliseconds) / timer.period) + 1) * timer.period * TimeSpan.TicksPerMillisecond); } /* do not increment here, since the time might have changed with years this would do a long loop here while(next <= now) { next += new TimeSpan (timer.period * TimeSpan.TicksPerMillisecond); } */ timer.nextDue = next; timer.AddTimer(); } } } return activated; }
// Remove the timer from the display's timer queue. private void RemoveTimer() { lock(dpy) { if(onDisplayQueue) { if(next != null) { next.prev = prev; } if(prev != null) { prev.next = next; } else { dpy.timerQueue = next; } onDisplayQueue = false; next = null; prev = null; } } }
// Add the timer to the display's timer queue. private void AddTimer() { lock(dpy) { if(!onDisplayQueue) { Timer current = dpy.timerQueue; Timer prev = null; int iCount = 0; while(current != null && current.nextDue <= nextDue) { prev = current; current = current.next; } this.next = current; this.prev = prev; if(current != null) { current.prev = this; } if(prev != null) { prev.next = this; } else { dpy.timerQueue = this; } onDisplayQueue = true; } } }